API: Show API actions alongside SWIG action plugins in preferences

This commit is contained in:
Jon Evans 2024-12-14 18:00:07 -05:00
parent ac73616e7c
commit 9eda526871
20 changed files with 862 additions and 567 deletions

View File

@ -208,6 +208,13 @@ wxString API_PLUGIN::BasePath() const
} }
wxString API_PLUGIN::ActionSettingsKey( const PLUGIN_ACTION& aAction ) const
{
return Identifier() + "." + aAction.identifier;
}
std::optional<PLUGIN_ACTION> API_PLUGIN::createActionFromJson( const nlohmann::json& aJson ) std::optional<PLUGIN_ACTION> API_PLUGIN::createActionFromJson( const nlohmann::json& aJson )
{ {
// TODO move to tl::expected and give user feedback about parse errors // TODO move to tl::expected and give user feedback about parse errors

View File

@ -132,6 +132,15 @@ void API_PLUGIN_MANAGER::ReloadPlugins()
} }
std::optional<const PLUGIN_ACTION*> API_PLUGIN_MANAGER::GetAction( const wxString& aIdentifier )
{
if( !m_actionsCache.count( aIdentifier ) )
return std::nullopt;
return m_actionsCache.at( aIdentifier );
}
void API_PLUGIN_MANAGER::InvokeAction( const wxString& aIdentifier ) void API_PLUGIN_MANAGER::InvokeAction( const wxString& aIdentifier )
{ {
if( !m_actionsCache.count( aIdentifier ) ) if( !m_actionsCache.count( aIdentifier ) )

View File

@ -1365,19 +1365,72 @@ bool EDA_DRAW_FRAME::SaveCanvasImageToFile( const wxString& aFileName,
} }
bool EDA_DRAW_FRAME::IsPluginActionButtonVisible( const PLUGIN_ACTION& aAction,
APP_SETTINGS_BASE* aCfg )
{
wxCHECK( aCfg, aAction.show_button );
for( const auto& [identifier, visible] : aCfg->m_Plugins.actions )
{
if( identifier == aAction.identifier )
return visible;
}
return aAction.show_button;
}
std::vector<const PLUGIN_ACTION*> EDA_DRAW_FRAME::GetOrderedPluginActions(
PLUGIN_ACTION_SCOPE aScope, APP_SETTINGS_BASE* aCfg )
{
std::vector<const PLUGIN_ACTION*> actions;
wxCHECK( aCfg, actions );
#ifdef KICAD_IPC_API
API_PLUGIN_MANAGER& mgr = Pgm().GetPluginManager();
std::vector<const PLUGIN_ACTION*> unsorted = mgr.GetActionsForScope( aScope );
std::map<wxString, const PLUGIN_ACTION*> actionMap;
std::set<const PLUGIN_ACTION*> handled;
for( const PLUGIN_ACTION* action : unsorted )
actionMap[action->identifier] = action;
for( const auto& identifier : aCfg->m_Plugins.actions | std::views::keys )
{
if( actionMap.contains( identifier ) )
{
const PLUGIN_ACTION* action = actionMap[ identifier ];
actions.emplace_back( action );
handled.insert( action );
}
}
for( const auto& action : actionMap | std::views::values )
{
if( !handled.contains( action ) )
actions.emplace_back( action );
}
#endif
return actions;
}
void EDA_DRAW_FRAME::addApiPluginTools() void EDA_DRAW_FRAME::addApiPluginTools()
{ {
#ifdef KICAD_IPC_API #ifdef KICAD_IPC_API
// TODO: Add user control over visibility and order
API_PLUGIN_MANAGER& mgr = Pgm().GetPluginManager(); API_PLUGIN_MANAGER& mgr = Pgm().GetPluginManager();
mgr.ButtonBindings().clear(); mgr.ButtonBindings().clear();
std::vector<const PLUGIN_ACTION*> actions = mgr.GetActionsForScope( PluginActionScope() ); std::vector<const PLUGIN_ACTION*> actions =
GetOrderedPluginActions( PluginActionScope(), config() );
for( auto& action : actions ) for( const PLUGIN_ACTION* action : actions )
{ {
if( !action->show_button ) if( !IsPluginActionButtonVisible( *action, config() ) )
continue; continue;
const wxBitmapBundle& icon = KIPLATFORM::UI::IsDarkTheme() && action->icon_dark.IsOk() const wxBitmapBundle& icon = KIPLATFORM::UI::IsDarkTheme() && action->icon_dark.IsOk()

View File

@ -41,6 +41,7 @@ APP_SETTINGS_BASE::APP_SETTINGS_BASE( const std::string& aFilename, int aSchemaV
m_Printing(), m_Printing(),
m_SearchPane(), m_SearchPane(),
m_System(), m_System(),
m_Plugins(),
m_Window(), m_Window(),
m_appSettingsSchemaVersion( aSchemaVersion ) m_appSettingsSchemaVersion( aSchemaVersion )
{ {
@ -168,6 +169,39 @@ APP_SETTINGS_BASE::APP_SETTINGS_BASE( const std::string& aFilename, int aSchemaV
m_params.emplace_back( new PARAM<bool>( "system.show_import_issues", m_params.emplace_back( new PARAM<bool>( "system.show_import_issues",
&m_System.show_import_issues, true ) ); &m_System.show_import_issues, true ) );
m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "plugins.actions",
[&]() -> nlohmann::json
{
nlohmann::json js = nlohmann::json::array();
for( const auto& [identifier, visible] : m_Plugins.actions )
js.push_back( nlohmann::json( { { identifier.ToUTF8(), visible } } ) );
return js;
},
[&]( const nlohmann::json& aObj )
{
m_Plugins.actions.clear();
if( !aObj.is_array() )
{
return;
}
for( const auto& entry : aObj )
{
if( entry.empty() || !entry.is_object() )
continue;
for( const auto& pair : entry.items() )
{
m_Plugins.actions.emplace_back( std::make_pair(
wxString( pair.key().c_str(), wxConvUTF8 ), pair.value() ) );
}
}
},
nlohmann::json::array() ) );
m_params.emplace_back( new PARAM<wxString>( "appearance.color_theme", m_params.emplace_back( new PARAM<wxString>( "appearance.color_theme",
&m_ColorTheme, COLOR_SETTINGS::COLOR_BUILTIN_DEFAULT ) ); &m_ColorTheme, COLOR_SETTINGS::COLOR_BUILTIN_DEFAULT ) );

View File

@ -29,6 +29,7 @@
#include <wx/dc.h> #include <wx/dc.h>
#include <bitmaps.h> #include <bitmaps.h>
#include <kiplatform/ui.h>
GRID_CELL_ICON_TEXT_RENDERER::GRID_CELL_ICON_TEXT_RENDERER( const std::vector<BITMAPS>& icons, GRID_CELL_ICON_TEXT_RENDERER::GRID_CELL_ICON_TEXT_RENDERER( const std::vector<BITMAPS>& icons,
@ -39,6 +40,14 @@ GRID_CELL_ICON_TEXT_RENDERER::GRID_CELL_ICON_TEXT_RENDERER( const std::vector<BI
} }
GRID_CELL_ICON_TEXT_RENDERER::GRID_CELL_ICON_TEXT_RENDERER( const wxBitmapBundle& aIcon,
wxSize aPreferredIconSize ) :
m_icon( aIcon ),
m_iconSize( aPreferredIconSize )
{
}
void GRID_CELL_ICON_TEXT_RENDERER::Draw( wxGrid& aGrid, wxGridCellAttr& aAttr, wxDC& aDC, void GRID_CELL_ICON_TEXT_RENDERER::Draw( wxGrid& aGrid, wxGridCellAttr& aAttr, wxDC& aDC,
const wxRect& aRect, int aRow, int aCol, bool isSelected ) const wxRect& aRect, int aRow, int aCol, bool isSelected )
{ {
@ -51,17 +60,18 @@ void GRID_CELL_ICON_TEXT_RENDERER::Draw( wxGrid& aGrid, wxGridCellAttr& aAttr, w
wxGridCellRenderer::Draw( aGrid, aAttr, aDC, aRect, aRow, aCol, isSelected ); wxGridCellRenderer::Draw( aGrid, aAttr, aDC, aRect, aRow, aCol, isSelected );
// draw the icon // draw the icon
// note that the set of icons might be smaller than the set of labels if the last
// label is <...>.
int position = m_names.Index( value );
int leftCut = aDC.FromDIP( 4 ); int leftCut = aDC.FromDIP( 4 );
if( position < (int) m_icons.size() && position != wxNOT_FOUND ) if( m_icon.IsOk() )
{ {
wxBitmapBundle bundle = KiBitmapBundle( m_icons[position] ); wxSize size = m_iconSize == wxDefaultSize
? m_icon.GetPreferredBitmapSizeAtScale( aDC.GetContentScaleFactor() )
: m_iconSize;
double scale = KIPLATFORM::UI::GetPixelScaleFactor( &aGrid );
wxBitmap bitmap = m_icon.GetBitmap( size * scale );
wxBitmap bitmap = bundle.GetBitmap( if( bitmap.IsOk() )
bundle.GetPreferredBitmapSizeAtScale( aDC.GetContentScaleFactor() ) ); bitmap.SetScaleFactor( scale );
aDC.DrawBitmap( bitmap, aDC.DrawBitmap( bitmap,
rect.GetLeft() + leftCut, rect.GetLeft() + leftCut,
@ -70,14 +80,35 @@ void GRID_CELL_ICON_TEXT_RENDERER::Draw( wxGrid& aGrid, wxGridCellAttr& aAttr, w
leftCut += bitmap.GetLogicalWidth(); leftCut += bitmap.GetLogicalWidth();
} }
else // still need a bitmap to fetch the width else
{ {
wxBitmapBundle bundle = KiBitmapBundle( m_icons[0] ); // note that the set of icons might be smaller than the set of labels if the last
// label is <...>.
int position = m_names.Index( value );
wxBitmap bitmap = bundle.GetBitmap( if( position < (int) m_icons.size() && position != wxNOT_FOUND )
bundle.GetPreferredBitmapSizeAtScale( aDC.GetContentScaleFactor() ) ); {
wxBitmapBundle bundle = KiBitmapBundle( m_icons[position] );
leftCut += bitmap.GetLogicalWidth(); wxBitmap bitmap = bundle.GetBitmap(
bundle.GetPreferredBitmapSizeAtScale( aDC.GetContentScaleFactor() ) );
aDC.DrawBitmap( bitmap,
rect.GetLeft() + leftCut,
rect.GetTop() + ( rect.GetHeight() - bitmap.GetLogicalHeight() ) / 2,
true );
leftCut += bitmap.GetLogicalWidth();
}
else // still need a bitmap to fetch the width
{
wxBitmapBundle bundle = KiBitmapBundle( m_icons[0] );
wxBitmap bitmap = bundle.GetBitmap(
bundle.GetPreferredBitmapSizeAtScale( aDC.GetContentScaleFactor() ) );
leftCut += bitmap.GetLogicalWidth();
}
} }
leftCut += aDC.FromDIP( 4 ); leftCut += aDC.FromDIP( 4 );
@ -90,19 +121,34 @@ void GRID_CELL_ICON_TEXT_RENDERER::Draw( wxGrid& aGrid, wxGridCellAttr& aAttr, w
aGrid.DrawTextRectangle( aDC, value, rect, wxALIGN_LEFT, wxALIGN_CENTRE ); aGrid.DrawTextRectangle( aDC, value, rect, wxALIGN_LEFT, wxALIGN_CENTRE );
} }
wxSize GRID_CELL_ICON_TEXT_RENDERER::GetBestSize( wxGrid& grid, wxGridCellAttr& attr, wxDC& dc, wxSize GRID_CELL_ICON_TEXT_RENDERER::GetBestSize( wxGrid& grid, wxGridCellAttr& attr, wxDC& dc,
int row, int col ) int row, int col )
{ {
int bmpIdx = ( row < (int) m_icons.size() && row >= 0 ) ? row : 0; wxBitmap bitmap;
wxBitmapBundle bundle = KiBitmapBundle( m_icons[bmpIdx] ); wxSize bitmapSize;
wxBitmap bitmap = if( m_icon.IsOk() )
bundle.GetBitmap( bundle.GetPreferredBitmapSizeAtScale( dc.GetContentScaleFactor() ) ); {
bitmapSize = m_iconSize == wxDefaultSize
? m_icon.GetPreferredBitmapSizeAtScale( dc.GetContentScaleFactor() )
: m_iconSize;
}
else
{
int bmpIdx = ( row < (int) m_icons.size() && row >= 0 ) ? row : 0;
wxBitmapBundle bundle = KiBitmapBundle( m_icons[bmpIdx] );
bitmap = bundle.GetBitmap(
bundle.GetPreferredBitmapSizeAtScale( dc.GetContentScaleFactor() ) );
bitmapSize = wxSize( bitmap.GetLogicalWidth(), -1 );
}
wxString text = grid.GetCellValue( row, col ); wxString text = grid.GetCellValue( row, col );
wxSize size = wxGridCellStringRenderer::DoGetBestSize( attr, dc, text ); wxSize size = wxGridCellStringRenderer::DoGetBestSize( attr, dc, text );
size.x += bitmap.GetLogicalWidth() + dc.FromDIP( 8 ); size.x += bitmapSize.x + dc.FromDIP( 8 );
size.y = std::max( size.y, bitmapSize.y + dc.FromDIP( 2 ) );
return size; return size;
} }

View File

@ -118,6 +118,8 @@ public:
const std::vector<PLUGIN_ACTION>& Actions() const; const std::vector<PLUGIN_ACTION>& Actions() const;
wxString ActionSettingsKey( const PLUGIN_ACTION& aAction ) const;
private: private:
friend struct API_PLUGIN_CONFIG; friend struct API_PLUGIN_CONFIG;

View File

@ -46,6 +46,8 @@ public:
void InvokeAction( const wxString& aIdentifier ); void InvokeAction( const wxString& aIdentifier );
std::optional<const PLUGIN_ACTION*> GetAction( const wxString& aIdentifier );
std::vector<const PLUGIN_ACTION*> GetActionsForScope( PLUGIN_ACTION_SCOPE aScope ); std::vector<const PLUGIN_ACTION*> GetActionsForScope( PLUGIN_ACTION_SCOPE aScope );
std::map<int, wxString>& ButtonBindings() { return m_buttonBindings; } std::map<int, wxString>& ButtonBindings() { return m_buttonBindings; }

View File

@ -512,6 +512,16 @@ public:
virtual PLUGIN_ACTION_SCOPE PluginActionScope() const { return PLUGIN_ACTION_SCOPE::INVALID; } virtual PLUGIN_ACTION_SCOPE PluginActionScope() const { return PLUGIN_ACTION_SCOPE::INVALID; }
static bool IsPluginActionButtonVisible( const PLUGIN_ACTION& aAction, APP_SETTINGS_BASE* aCfg );
/**
* Return ordered list of plugin actions for display in the toolbar
* Must be static at the moment because this needs to be called from the preferences dialog,
* which can exist without the frame in question actually being created.
* @param aCfg is the settings to read the plugin ordering from
*/
static std::vector<const PLUGIN_ACTION*> GetOrderedPluginActions( PLUGIN_ACTION_SCOPE aScope,
APP_SETTINGS_BASE* aCfg );
DECLARE_EVENT_TABLE() DECLARE_EVENT_TABLE()

View File

@ -158,6 +158,12 @@ public:
bool show_import_issues; bool show_import_issues;
}; };
struct PLUGINS
{
/// Ordered list of plugin actions mapped to whether or not they are shown in the toolbar
std::vector<std::pair<wxString, bool>> actions;
};
APP_SETTINGS_BASE( const std::string& aFilename, int aSchemaVersion ); APP_SETTINGS_BASE( const std::string& aFilename, int aSchemaVersion );
virtual ~APP_SETTINGS_BASE() {} virtual ~APP_SETTINGS_BASE() {}
@ -183,6 +189,8 @@ public:
SYSTEM m_System; SYSTEM m_System;
PLUGINS m_Plugins;
WINDOW_SETTINGS m_Window; WINDOW_SETTINGS m_Window;
/// Active color theme name /// Active color theme name

View File

@ -39,8 +39,20 @@ enum class BITMAPS : unsigned int;
class GRID_CELL_ICON_TEXT_RENDERER : public wxGridCellStringRenderer class GRID_CELL_ICON_TEXT_RENDERER : public wxGridCellStringRenderer
{ {
public: public:
/**
* Construct a renderer that maps a list of icons from the bitmap system to a list of strings
* @param icons is a list of possible icons to render
* @param names is a list of names to render - must be the same length as icons
*/
GRID_CELL_ICON_TEXT_RENDERER( const std::vector<BITMAPS>& icons, const wxArrayString& names ); GRID_CELL_ICON_TEXT_RENDERER( const std::vector<BITMAPS>& icons, const wxArrayString& names );
/**
* Construct a renderer that renders a single icon next to the cell's value text
* @param aIcon is the icon to render next to the cell's value
*/
GRID_CELL_ICON_TEXT_RENDERER( const wxBitmapBundle& aIcon,
wxSize aPreferredIconSize = wxDefaultSize );
void Draw( wxGrid& aGrid, wxGridCellAttr& aAttr, wxDC& aDC, void Draw( wxGrid& aGrid, wxGridCellAttr& aAttr, wxDC& aDC,
const wxRect& aRect, int aRow, int aCol, bool isSelected ) override; const wxRect& aRect, int aRow, int aCol, bool isSelected ) override;
wxSize GetBestSize( wxGrid & grid, wxGridCellAttr & attr, wxDC & dc, int row, int col ) override; wxSize GetBestSize( wxGrid & grid, wxGridCellAttr & attr, wxDC & dc, int row, int col ) override;
@ -48,6 +60,10 @@ public:
private: private:
std::vector<BITMAPS> m_icons; std::vector<BITMAPS> m_icons;
wxArrayString m_names; wxArrayString m_names;
// For single-icon mode
wxBitmapBundle m_icon;
wxSize m_iconSize;
}; };
//---- Grid helpers: custom wxGridCellRenderer that renders just an icon ---------------- //---- Grid helpers: custom wxGridCellRenderer that renders just an icon ----------------

View File

@ -53,6 +53,11 @@ public:
virtual wxString GetName() = 0; virtual wxString GetName() = 0;
/**
* @return the name of the Python class defining the action
*/
virtual wxString GetClassName() = 0;
/** /**
* @return a description of the action plugin. * @return a description of the action plugin.
*/ */
@ -212,4 +217,7 @@ private:
static bool m_actionRunning; static bool m_actionRunning;
}; };
typedef std::variant<ACTION_PLUGIN*, const PLUGIN_ACTION*> LEGACY_OR_API_PLUGIN;
#endif /* PCBNEW_ACTION_PLUGINS_H */ #endif /* PCBNEW_ACTION_PLUGINS_H */

View File

@ -19,15 +19,19 @@
*/ */
#include <action_plugin.h> #include <action_plugin.h>
#include <api/api_plugin.h>
#include <bitmaps.h> #include <bitmaps.h>
#include <dialog_footprint_wizard_list.h> #include <dialog_footprint_wizard_list.h>
#include <grid_tricks.h> #include <grid_tricks.h>
#include <kiface_base.h> #include <kiface_base.h>
#include <kiplatform/ui.h>
#include <panel_pcbnew_action_plugins.h> #include <panel_pcbnew_action_plugins.h>
#include <pcb_edit_frame.h> #include <pcb_edit_frame.h>
#include <python/scripting/pcbnew_scripting.h> #include <python/scripting/pcbnew_scripting.h>
#include <pcb_scripting_tool.h> #include <pcb_scripting_tool.h>
#include <pcbnew_settings.h> #include <pcbnew_settings.h>
#include <pgm_base.h>
#include <api/api_plugin_manager.h>
#include <widgets/grid_icon_text_helpers.h> #include <widgets/grid_icon_text_helpers.h>
#include <widgets/paged_dialog.h> #include <widgets/paged_dialog.h>
#include <widgets/wx_grid.h> #include <widgets/wx_grid.h>
@ -39,7 +43,7 @@
PANEL_PCBNEW_ACTION_PLUGINS::PANEL_PCBNEW_ACTION_PLUGINS( wxWindow* aParent ) : PANEL_PCBNEW_ACTION_PLUGINS::PANEL_PCBNEW_ACTION_PLUGINS( wxWindow* aParent ) :
PANEL_PCBNEW_ACTION_PLUGINS_BASE( aParent ) PANEL_PCBNEW_ACTION_PLUGINS_BASE( aParent )
{ {
m_genericIcon = KiBitmap( BITMAPS::puzzle_piece ); m_genericIcon = KiBitmapBundle( BITMAPS::puzzle_piece );
m_grid->PushEventHandler( new GRID_TRICKS( m_grid ) ); m_grid->PushEventHandler( new GRID_TRICKS( m_grid ) );
m_grid->SetUseNativeColLabels(); m_grid->SetUseNativeColLabels();
@ -118,10 +122,9 @@ void PANEL_PCBNEW_ACTION_PLUGINS::SwapRows( int aRowA, int aRowB )
{ {
m_grid->Freeze(); m_grid->Freeze();
// Swap all columns except icon
wxString tempStr; wxString tempStr;
for( int column = 1; column < m_grid->GetNumberCols(); column++ ) for( int column = 0; column < m_grid->GetNumberCols(); column++ )
{ {
tempStr = m_grid->GetCellValue( aRowA, column ); tempStr = m_grid->GetCellValue( aRowA, column );
m_grid->SetCellValue( aRowA, column, m_grid->GetCellValue( aRowB, column ) ); m_grid->SetCellValue( aRowA, column, m_grid->GetCellValue( aRowB, column ) );
@ -129,9 +132,10 @@ void PANEL_PCBNEW_ACTION_PLUGINS::SwapRows( int aRowA, int aRowB )
} }
// Swap icon column renderers // Swap icon column renderers
auto cellRenderer = m_grid->GetCellRenderer( aRowA, COLUMN_ICON ); auto cellRenderer = m_grid->GetCellRenderer( aRowA, COLUMN_ACTION_NAME );
m_grid->SetCellRenderer( aRowA, COLUMN_ICON, m_grid->GetCellRenderer( aRowB, COLUMN_ICON ) ); m_grid->SetCellRenderer( aRowA, COLUMN_ACTION_NAME,
m_grid->SetCellRenderer( aRowB, COLUMN_ICON, cellRenderer ); m_grid->GetCellRenderer( aRowB, COLUMN_ACTION_NAME ) );
m_grid->SetCellRenderer( aRowB, COLUMN_ACTION_NAME, cellRenderer );
m_grid->Thaw(); m_grid->Thaw();
} }
@ -149,15 +153,27 @@ bool PANEL_PCBNEW_ACTION_PLUGINS::TransferDataFromWindow()
PCBNEW_SETTINGS* settings = dynamic_cast<PCBNEW_SETTINGS*>( Kiface().KifaceSettings() ); PCBNEW_SETTINGS* settings = dynamic_cast<PCBNEW_SETTINGS*>( Kiface().KifaceSettings() );
wxASSERT( settings ); wxASSERT( settings );
API_PLUGIN_MANAGER& mgr = Pgm().GetPluginManager();
if( settings ) if( settings )
{ {
settings->m_VisibleActionPlugins.clear(); settings->m_VisibleActionPlugins.clear();
settings->m_Plugins.actions.clear();
for( int ii = 0; ii < m_grid->GetNumberRows(); ii++ ) for( int ii = 0; ii < m_grid->GetNumberRows(); ii++ )
{ {
settings->m_VisibleActionPlugins.emplace_back( std::make_pair( wxString id = m_grid->GetCellValue( ii, COLUMN_SETTINGS_IDENTIFIER );
m_grid->GetCellValue( ii, COLUMN_PATH ),
m_grid->GetCellValue( ii, COLUMN_VISIBLE ) == wxT( "1" ) ) ); if( mgr.GetAction( id ) != std::nullopt )
{
settings->m_Plugins.actions.emplace_back( std::make_pair(
id, m_grid->GetCellValue( ii, COLUMN_VISIBLE ) == wxT( "1" ) ) );
}
else
{
settings->m_VisibleActionPlugins.emplace_back( std::make_pair(
id, m_grid->GetCellValue( ii, COLUMN_VISIBLE ) == wxT( "1" ) ) );
}
} }
} }
@ -171,30 +187,66 @@ bool PANEL_PCBNEW_ACTION_PLUGINS::TransferDataToWindow()
m_grid->ClearRows(); m_grid->ClearRows();
const auto& orderedPlugins = PCB_EDIT_FRAME::GetOrderedActionPlugins(); const std::vector<LEGACY_OR_API_PLUGIN>& orderedPlugins =
PCB_EDIT_FRAME::GetOrderedActionPlugins();
m_grid->AppendRows( orderedPlugins.size() ); m_grid->AppendRows( orderedPlugins.size() );
int size = Pgm().GetCommonSettings()->m_Appearance.toolbar_icon_size;
wxSize iconSize( size, size );
for( size_t row = 0; row < orderedPlugins.size(); row++ ) for( size_t row = 0; row < orderedPlugins.size(); row++ )
{ {
ACTION_PLUGIN* ap = orderedPlugins[row]; if( std::holds_alternative<ACTION_PLUGIN*>( orderedPlugins[row] ) )
{
auto ap = std::get<ACTION_PLUGIN*>( orderedPlugins[row] );
// Icon // Icon
m_grid->SetCellRenderer( row, COLUMN_ICON, new GRID_CELL_ICON_RENDERER( m_grid->SetCellRenderer( row, COLUMN_ACTION_NAME,
ap->iconBitmap.IsOk() ? ap->iconBitmap : m_genericIcon ) ); new GRID_CELL_ICON_TEXT_RENDERER( ap->iconBitmap.IsOk()
? wxBitmapBundle( ap->iconBitmap )
: m_genericIcon,
iconSize ) );
m_grid->SetCellValue( row, COLUMN_ACTION_NAME, ap->GetName() );
m_grid->SetCellValue( row, COLUMN_SETTINGS_IDENTIFIER, ap->GetPluginPath() );
// Toolbar button checkbox // Toolbar button checkbox
m_grid->SetCellRenderer( row, COLUMN_VISIBLE, new wxGridCellBoolRenderer() ); m_grid->SetCellRenderer( row, COLUMN_VISIBLE, new wxGridCellBoolRenderer() );
m_grid->SetCellAlignment( row, COLUMN_VISIBLE, wxALIGN_CENTER, wxALIGN_CENTER ); m_grid->SetCellAlignment( row, COLUMN_VISIBLE, wxALIGN_CENTER, wxALIGN_CENTER );
bool show = PCB_EDIT_FRAME::GetActionPluginButtonVisible( ap->GetPluginPath(), bool show = PCB_EDIT_FRAME::GetActionPluginButtonVisible( ap->GetPluginPath(),
ap->GetShowToolbarButton() ); ap->GetShowToolbarButton() );
m_grid->SetCellValue( row, COLUMN_VISIBLE, show ? wxT( "1" ) : wxEmptyString ); m_grid->SetCellValue( row, COLUMN_VISIBLE, show ? wxT( "1" ) : wxEmptyString );
m_grid->SetCellValue( row, COLUMN_NAME, ap->GetName() ); m_grid->SetCellValue( row, COLUMN_PLUGIN_NAME, ap->GetClassName() );
m_grid->SetCellValue( row, COLUMN_CATEGORY, ap->GetCategoryName() ); m_grid->SetCellValue( row, COLUMN_DESCRIPTION, ap->GetDescription() );
m_grid->SetCellValue( row, COLUMN_DESCRIPTION, ap->GetDescription() ); }
m_grid->SetCellValue( row, COLUMN_PATH, ap->GetPluginPath() ); else
{
auto action = std::get<const PLUGIN_ACTION*>( orderedPlugins[row] );
const wxBitmapBundle& icon = KIPLATFORM::UI::IsDarkTheme() && action->icon_dark.IsOk()
? action->icon_dark
: action->icon_light;
// Icon
m_grid->SetCellRenderer( row, COLUMN_ACTION_NAME, new GRID_CELL_ICON_TEXT_RENDERER(
icon.IsOk() ? icon : m_genericIcon, iconSize ) );
m_grid->SetCellValue( row, COLUMN_ACTION_NAME, action->name );
m_grid->SetCellValue( row, COLUMN_SETTINGS_IDENTIFIER, action->identifier );
// Toolbar button checkbox
m_grid->SetCellRenderer( row, COLUMN_VISIBLE, new wxGridCellBoolRenderer() );
m_grid->SetCellAlignment( row, COLUMN_VISIBLE, wxALIGN_CENTER, wxALIGN_CENTER );
bool show = PCB_EDIT_FRAME::GetActionPluginButtonVisible( action->identifier,
action->show_button );
m_grid->SetCellValue( row, COLUMN_VISIBLE, show ? wxT( "1" ) : wxEmptyString );
m_grid->SetCellValue( row, COLUMN_PLUGIN_NAME, action->plugin.Name() );
m_grid->SetCellValue( row, COLUMN_DESCRIPTION, action->description );
}
} }
for( int col = 0; col < m_grid->GetNumberCols(); col++ ) for( int col = 0; col < m_grid->GetNumberCols(); col++ )
@ -209,6 +261,8 @@ bool PANEL_PCBNEW_ACTION_PLUGINS::TransferDataToWindow()
} }
m_grid->AutoSizeRows(); m_grid->AutoSizeRows();
m_grid->AutoSizeColumns();
m_grid->HideCol( COLUMN_SETTINGS_IDENTIFIER );
m_grid->Thaw(); m_grid->Thaw();

View File

@ -63,15 +63,14 @@ private:
enum GRID_COLUMNS enum GRID_COLUMNS
{ {
COLUMN_ICON, COLUMN_ACTION_NAME,
COLUMN_VISIBLE, COLUMN_VISIBLE,
COLUMN_NAME, COLUMN_PLUGIN_NAME,
COLUMN_CATEGORY,
COLUMN_DESCRIPTION, COLUMN_DESCRIPTION,
COLUMN_PATH COLUMN_SETTINGS_IDENTIFIER,
}; };
wxBitmap m_genericIcon; wxBitmapBundle m_genericIcon;
void SwapRows( int aRowA, int aRowB ); void SwapRows( int aRowA, int aRowB );
void SelectRow( int aRow ); void SelectRow( int aRow );

View File

@ -1,5 +1,5 @@
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 4.0.0-0-g0efcecf) // C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6)
// http://www.wxformbuilder.org/ // http://www.wxformbuilder.org/
// //
// PLEASE DO *NOT* EDIT THIS FILE! // PLEASE DO *NOT* EDIT THIS FILE!
@ -14,6 +14,9 @@
PANEL_PCBNEW_ACTION_PLUGINS_BASE::PANEL_PCBNEW_ACTION_PLUGINS_BASE( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name ) : wxPanel( parent, id, pos, size, style, name ) PANEL_PCBNEW_ACTION_PLUGINS_BASE::PANEL_PCBNEW_ACTION_PLUGINS_BASE( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name ) : wxPanel( parent, id, pos, size, style, name )
{ {
wxBoxSizer* bSizer4;
bSizer4 = new wxBoxSizer( wxHORIZONTAL );
wxBoxSizer* bPanelSizer; wxBoxSizer* bPanelSizer;
bPanelSizer = new wxBoxSizer( wxVERTICAL ); bPanelSizer = new wxBoxSizer( wxVERTICAL );
@ -23,7 +26,7 @@ PANEL_PCBNEW_ACTION_PLUGINS_BASE::PANEL_PCBNEW_ACTION_PLUGINS_BASE( wxWindow* pa
m_grid = new WX_GRID( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); m_grid = new WX_GRID( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
// Grid // Grid
m_grid->CreateGrid( 3, 6 ); m_grid->CreateGrid( 3, 5 );
m_grid->EnableEditing( false ); m_grid->EnableEditing( false );
m_grid->EnableGridLines( true ); m_grid->EnableGridLines( true );
m_grid->EnableDragGridSize( false ); m_grid->EnableDragGridSize( false );
@ -33,12 +36,11 @@ PANEL_PCBNEW_ACTION_PLUGINS_BASE::PANEL_PCBNEW_ACTION_PLUGINS_BASE( wxWindow* pa
m_grid->AutoSizeColumns(); m_grid->AutoSizeColumns();
m_grid->EnableDragColMove( false ); m_grid->EnableDragColMove( false );
m_grid->EnableDragColSize( true ); m_grid->EnableDragColSize( true );
m_grid->SetColLabelValue( 0, _("Icon") ); m_grid->SetColLabelValue( 0, _("Action") );
m_grid->SetColLabelValue( 1, _("Show Button") ); m_grid->SetColLabelValue( 1, _("Show Button") );
m_grid->SetColLabelValue( 2, _("Name") ); m_grid->SetColLabelValue( 2, _("Plugin") );
m_grid->SetColLabelValue( 3, _("Category") ); m_grid->SetColLabelValue( 3, _("Description") );
m_grid->SetColLabelValue( 4, _("Description") ); m_grid->SetColLabelValue( 4, wxEmptyString );
m_grid->SetColLabelValue( 5, _("Path") );
m_grid->SetColLabelSize( wxGRID_AUTOSIZE ); m_grid->SetColLabelSize( wxGRID_AUTOSIZE );
m_grid->SetColLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER ); m_grid->SetColLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
@ -91,9 +93,12 @@ PANEL_PCBNEW_ACTION_PLUGINS_BASE::PANEL_PCBNEW_ACTION_PLUGINS_BASE( wxWindow* pa
bPanelSizer->Add( bButtonsSizer, 0, wxEXPAND|wxLEFT|wxTOP, 5 ); bPanelSizer->Add( bButtonsSizer, 0, wxEXPAND|wxLEFT|wxTOP, 5 );
this->SetSizer( bPanelSizer ); bSizer4->Add( bPanelSizer, 1, wxEXPAND, 5 );
this->SetSizer( bSizer4 );
this->Layout(); this->Layout();
bPanelSizer->Fit( this ); bSizer4->Fit( this );
// Connect Events // Connect Events
m_grid->Connect( wxEVT_GRID_CELL_LEFT_CLICK, wxGridEventHandler( PANEL_PCBNEW_ACTION_PLUGINS_BASE::OnGridCellClick ), NULL, this ); m_grid->Connect( wxEVT_GRID_CELL_LEFT_CLICK, wxGridEventHandler( PANEL_PCBNEW_ACTION_PLUGINS_BASE::OnGridCellClick ), NULL, this );

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 4.0.0-0-g0efcecf) // C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6)
// http://www.wxformbuilder.org/ // http://www.wxformbuilder.org/
// //
// PLEASE DO *NOT* EDIT THIS FILE! // PLEASE DO *NOT* EDIT THIS FILE!

View File

@ -24,6 +24,7 @@
#include "pcb_base_edit_frame.h" #include "pcb_base_edit_frame.h"
#include "zones.h" #include "zones.h"
#include <mail_type.h> #include <mail_type.h>
#include <settings/app_settings.h>
class ACTION_PLUGIN; class ACTION_PLUGIN;
class PCB_SCREEN; class PCB_SCREEN;
@ -202,9 +203,9 @@ public:
/** /**
* Return ordered list of plugins in sequence in which they should appear on toolbar or * Return ordered list of plugins in sequence in which they should appear on toolbar or
* in settings * in settings. Handles both legacy (SWIG) and API plugins, so returns a heterogenous list.
*/ */
static std::vector<ACTION_PLUGIN*> GetOrderedActionPlugins(); static std::vector<std::variant<ACTION_PLUGIN*, const PLUGIN_ACTION*>> GetOrderedActionPlugins();
void SaveProjectLocalSettings() override; void SaveProjectLocalSettings() override;

View File

@ -171,6 +171,14 @@ wxString PYTHON_ACTION_PLUGIN::GetCategoryName()
} }
wxString PYTHON_ACTION_PLUGIN::GetClassName()
{
PyLOCK lock;
return CallRetStrMethod( "GetClassName" );
}
wxString PYTHON_ACTION_PLUGIN::GetName() wxString PYTHON_ACTION_PLUGIN::GetName()
{ {
PyLOCK lock; PyLOCK lock;
@ -496,10 +504,16 @@ void PCB_EDIT_FRAME::buildActionPluginMenus( ACTION_MENU* actionMenu )
void PCB_EDIT_FRAME::AddActionPluginTools() void PCB_EDIT_FRAME::AddActionPluginTools()
{ {
bool need_separator = true; bool need_separator = true;
const std::vector<ACTION_PLUGIN*>& orderedPlugins = GetOrderedActionPlugins(); const std::vector<LEGACY_OR_API_PLUGIN>& orderedPlugins = GetOrderedActionPlugins();
for( ACTION_PLUGIN* ap : orderedPlugins ) for( const auto& entry : orderedPlugins )
{ {
// API plugins are handled by EDA_BASE_FRAME
if( !std::holds_alternative<ACTION_PLUGIN*>( entry ) )
continue;
ACTION_PLUGIN* ap = std::get<ACTION_PLUGIN*>( entry );
if( GetActionPluginButtonVisible( ap->GetPluginPath(), ap->GetShowToolbarButton() ) ) if( GetActionPluginButtonVisible( ap->GetPluginPath(), ap->GetShowToolbarButton() ) )
{ {
if( need_separator ) if( need_separator )
@ -529,13 +543,13 @@ void PCB_EDIT_FRAME::AddActionPluginTools()
} }
std::vector<ACTION_PLUGIN*> PCB_EDIT_FRAME::GetOrderedActionPlugins() std::vector<LEGACY_OR_API_PLUGIN> PCB_EDIT_FRAME::GetOrderedActionPlugins()
{ {
SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager(); SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
PCBNEW_SETTINGS* cfg = mgr.GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" ); PCBNEW_SETTINGS* cfg = mgr.GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" );
std::vector<ACTION_PLUGIN*> plugins; std::vector<ACTION_PLUGIN*> plugins;
std::vector<ACTION_PLUGIN*> orderedPlugins; std::vector<LEGACY_OR_API_PLUGIN> orderedPlugins;
for( int i = 0; i < ACTION_PLUGINS::GetActionsCount(); i++ ) for( int i = 0; i < ACTION_PLUGINS::GetActionsCount(); i++ )
plugins.push_back( ACTION_PLUGINS::GetAction( i ) ); plugins.push_back( ACTION_PLUGINS::GetAction( i ) );
@ -560,6 +574,10 @@ std::vector<ACTION_PLUGIN*> PCB_EDIT_FRAME::GetOrderedActionPlugins()
for( auto remaining_plugin : plugins ) for( auto remaining_plugin : plugins )
orderedPlugins.push_back( remaining_plugin ); orderedPlugins.push_back( remaining_plugin );
// Finally append API plugins
for( const PLUGIN_ACTION* action : GetOrderedPluginActions( PLUGIN_ACTION_SCOPE::PCB, cfg ) )
orderedPlugins.push_back( action );
return orderedPlugins; return orderedPlugins;
} }
@ -570,10 +588,16 @@ bool PCB_EDIT_FRAME::GetActionPluginButtonVisible( const wxString& aPluginPath,
SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager(); SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
PCBNEW_SETTINGS* cfg = mgr.GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" ); PCBNEW_SETTINGS* cfg = mgr.GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" );
for( const auto& entry : cfg->m_VisibleActionPlugins ) for( const auto& [path, visible] : cfg->m_VisibleActionPlugins )
{ {
if( entry.first == aPluginPath ) if( path == aPluginPath )
return entry.second; return visible;
}
for( const auto& [identifier, visible] : cfg->m_Plugins.actions )
{
if( identifier == aPluginPath )
return visible;
} }
// Plugin is not in settings, return default. // Plugin is not in settings, return default.

View File

@ -43,6 +43,7 @@ public:
PYTHON_ACTION_PLUGIN( PyObject* action ); PYTHON_ACTION_PLUGIN( PyObject* action );
~PYTHON_ACTION_PLUGIN(); ~PYTHON_ACTION_PLUGIN();
wxString GetCategoryName() override; wxString GetCategoryName() override;
wxString GetClassName() override;
wxString GetName() override; wxString GetName() override;
wxString GetDescription() override; wxString GetDescription() override;
bool GetShowToolbarButton() override; bool GetShowToolbarButton() override;

View File

@ -668,6 +668,9 @@ class ActionPlugin(KiCadPlugin, object):
self.category = "Undefined" self.category = "Undefined"
self.description = "" self.description = ""
def GetClassName(self):
return type(self).__name__
def GetName( self ): def GetName( self ):
return self.name return self.name