ADDED: Optional flashing with cross-probing

Sometimes it is hard to find an element especially if we do not zoom to
the part.  The flash should help draw your eye to the relevant part

Fixes https://gitlab.com/kicad/code/kicad/-/issues/19842
This commit is contained in:
Seth Hillbrand 2025-09-07 17:17:52 -07:00
parent f66cbaf43a
commit 0c35068e8d
16 changed files with 395 additions and 0 deletions

View File

@ -280,6 +280,9 @@ APP_SETTINGS_BASE::APP_SETTINGS_BASE( const std::string& aFilename, int aSchemaV
m_params.emplace_back( new PARAM<bool>( "cross_probing.auto_highlight",
&m_CrossProbing.auto_highlight, true ) );
m_params.emplace_back( new PARAM<bool>( "cross_probing.flash_selection",
&m_CrossProbing.flash_selection, false ) );
}

View File

@ -931,6 +931,26 @@ void SCH_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail )
items );
m_syncingPcbToSchSelection = false;
if( eeconfig()->m_CrossProbing.flash_selection )
{
wxLogTrace( "CROSS_PROBE_FLASH", "MAIL_SELECTION(_FORCE): flash enabled, items=%zu", items.size() );
if( items.empty() )
{
wxLogTrace( "CROSS_PROBE_FLASH", "MAIL_SELECTION(_FORCE): nothing to flash" );
}
else
{
std::vector<SCH_ITEM*> itemPtrs;
std::copy( items.begin(), items.end(), std::back_inserter( itemPtrs ) );
StartCrossProbeFlash( itemPtrs );
}
}
else
{
wxLogTrace( "CROSS_PROBE_FLASH", "MAIL_SELECTION(_FORCE): flash disabled" );
}
}
break;

View File

@ -75,6 +75,7 @@ void PANEL_EESCHEMA_DISPLAY_OPTIONS::loadEEschemaSettings( EESCHEMA_SETTINGS* cf
m_checkCrossProbeCenter->SetValue( cfg->m_CrossProbing.center_on_items );
m_checkCrossProbeZoom->SetValue( cfg->m_CrossProbing.zoom_to_fit );
m_checkCrossProbeAutoHighlight->SetValue( cfg->m_CrossProbing.auto_highlight );
m_checkCrossProbeFlash->SetValue( cfg->m_CrossProbing.flash_selection );
}
@ -117,6 +118,7 @@ bool PANEL_EESCHEMA_DISPLAY_OPTIONS::TransferDataFromWindow()
cfg->m_CrossProbing.center_on_items = m_checkCrossProbeCenter->GetValue();
cfg->m_CrossProbing.zoom_to_fit = m_checkCrossProbeZoom->GetValue();
cfg->m_CrossProbing.auto_highlight = m_checkCrossProbeAutoHighlight->GetValue();
cfg->m_CrossProbing.flash_selection = m_checkCrossProbeFlash->GetValue();
}
m_galOptsPanel->TransferDataFromWindow();

View File

@ -62,6 +62,10 @@ PANEL_EESCHEMA_DISPLAY_OPTIONS_BASE::PANEL_EESCHEMA_DISPLAY_OPTIONS_BASE( wxWind
bCrossProbingSizer->Add( m_checkCrossProbeAutoHighlight, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 );
m_checkCrossProbeFlash = new wxCheckBox( this, wxID_ANY, _("Flash cross-probed selection"), wxDefaultPosition, wxDefaultSize, 0 );
m_checkCrossProbeFlash->SetToolTip( _("Temporarily flash the newly cross-probed selection 3 times") );
bCrossProbingSizer->Add( m_checkCrossProbeFlash, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 );
bSizer8->Add( bCrossProbingSizer, 0, wxEXPAND|wxTOP|wxLEFT, 5 );

View File

@ -293,6 +293,71 @@
<property name="window_name"></property>
<property name="window_style"></property>
</object>
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxBOTTOM|wxRIGHT|wxLEFT</property>
<property name="proportion">0</property>
<object class="wxCheckBox" expanded="false">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer">0</property>
<property name="aui_name"></property>
<property name="aui_position">0</property>
<property name="aui_row">0</property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="checked">1</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Flash cross-probed selection</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_checkCrossProbeFlash</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip">Temporarily flash the newly cross-probed selection 3 times</property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
</object>
<object class="sizeritem" expanded="false">
<property name="border">5</property>

View File

@ -44,6 +44,7 @@ class PANEL_EESCHEMA_DISPLAY_OPTIONS_BASE : public RESETTABLE_PANEL
wxCheckBox* m_checkCrossProbeCenter;
wxCheckBox* m_checkCrossProbeZoom;
wxCheckBox* m_checkCrossProbeAutoHighlight;
wxCheckBox* m_checkCrossProbeFlash;
wxStaticText* m_appearanceLabel;
wxStaticLine* m_staticline1;
wxStaticText* m_defaultFontLabel;

View File

@ -104,6 +104,7 @@
#include <drawing_sheet/ds_proxy_view_item.h>
#include <project/project_local_settings.h>
#include <toolbars_sch_editor.h>
#include <wx/log.h>
#ifdef KICAD_IPC_API
#include <api/api_plugin_manager.h>
@ -157,6 +158,9 @@ SCH_EDIT_FRAME::SCH_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
m_syncingPcbToSchSelection = false;
m_aboutTitle = _HKI( "KiCad Schematic Editor" );
m_show_search = false;
// Ensure timer has an owner before binding so it generates events.
m_crossProbeFlashTimer.SetOwner( this );
Bind( wxEVT_TIMER, &SCH_EDIT_FRAME::OnCrossProbeFlashTimer, this, m_crossProbeFlashTimer.GetId() );
// Give an icon
wxIcon icon;
@ -434,6 +438,95 @@ SCH_EDIT_FRAME::SCH_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
Bind( EDA_EVT_CLOSE_DIALOG_SYMBOL_FIELDS_TABLE, &SCH_EDIT_FRAME::onCloseSymbolFieldsTableDialog, this );
}
void SCH_EDIT_FRAME::StartCrossProbeFlash( const std::vector<SCH_ITEM*>& aItems )
{
if( !eeconfig()->m_CrossProbing.flash_selection )
{
wxLogTrace( "CROSS_PROBE_FLASH", "StartCrossProbeFlash: aborted (setting disabled) items=%zu", aItems.size() );
return;
}
if( aItems.empty() )
{
wxLogTrace( "CROSS_PROBE_FLASH", "StartCrossProbeFlash: aborted (no items)" );
return;
}
if( m_crossProbeFlashing )
{
wxLogTrace( "CROSS_PROBE_FLASH", "StartCrossProbeFlash: restarting existing flash (phase=%d)", m_crossProbeFlashPhase );
m_crossProbeFlashTimer.Stop();
}
wxLogTrace( "CROSS_PROBE_FLASH", "StartCrossProbeFlash: starting with %zu items", aItems.size() );
m_crossProbeFlashItems.clear();
for( SCH_ITEM* it : aItems )
m_crossProbeFlashItems.push_back( it->m_Uuid );
m_crossProbeFlashPhase = 0;
m_crossProbeFlashing = true;
if( !m_crossProbeFlashTimer.GetOwner() )
m_crossProbeFlashTimer.SetOwner( this );
bool started = m_crossProbeFlashTimer.Start( 500, wxTIMER_CONTINUOUS );
wxLogTrace( "CROSS_PROBE_FLASH", "StartCrossProbeFlash: timer start=%d id=%d", (int) started, m_crossProbeFlashTimer.GetId() );
}
void SCH_EDIT_FRAME::OnCrossProbeFlashTimer( wxTimerEvent& aEvent )
{
wxLogTrace( "CROSS_PROBE_FLASH", "Timer(SCH) fired: phase=%d running=%d items=%zu", m_crossProbeFlashPhase, (int) m_crossProbeFlashing, m_crossProbeFlashItems.size() );
if( !m_crossProbeFlashing )
{
wxLogTrace( "CROSS_PROBE_FLASH", "Timer fired but not flashing (ignored)" );
return;
}
SCH_SELECTION_TOOL* selTool = GetToolManager()->GetTool<SCH_SELECTION_TOOL>();
if( !selTool )
return;
bool prevGuard = m_syncingPcbToSchSelection;
m_syncingPcbToSchSelection = true;
if( m_crossProbeFlashPhase % 2 == 0 )
{
selTool->ClearSelection( true );
wxLogTrace( "CROSS_PROBE_FLASH", "Phase %d: cleared selection", m_crossProbeFlashPhase );
}
else
{
for( const KIID& id : m_crossProbeFlashItems )
{
if( SCH_ITEM* item = Schematic().ResolveItem( id, nullptr, true ) )
selTool->AddItemToSel( item, true );
}
wxLogTrace( "CROSS_PROBE_FLASH", "Phase %d: restored %zu items", m_crossProbeFlashPhase, m_crossProbeFlashItems.size() );
}
if( GetCanvas() )
{
GetCanvas()->ForceRefresh();
wxLogTrace( "CROSS_PROBE_FLASH", "Phase %d: forced canvas refresh", m_crossProbeFlashPhase );
}
m_syncingPcbToSchSelection = prevGuard;
m_crossProbeFlashPhase++;
if( m_crossProbeFlashPhase > 6 )
{
for( const KIID& id : m_crossProbeFlashItems )
{
if( SCH_ITEM* item = Schematic().ResolveItem( id, nullptr, true ) )
selTool->AddItemToSel( item, true );
}
m_crossProbeFlashing = false;
m_crossProbeFlashTimer.Stop();
wxLogTrace( "CROSS_PROBE_FLASH", "Flashing complete. Final selection size=%zu", m_crossProbeFlashItems.size() );
}
}
SCH_EDIT_FRAME::~SCH_EDIT_FRAME()
{

View File

@ -989,6 +989,9 @@ private:
void CaptureHierarchyPaneSize();
void StartCrossProbeFlash( const std::vector<SCH_ITEM*>& aItems );
void OnCrossProbeFlashTimer( wxTimerEvent& aEvent );
private:
// The schematic editor control class should be able to access some internal
// functions of the editor frame.
@ -1015,6 +1018,11 @@ private:
wxTreeCtrl* m_netNavigator;
bool m_syncingPcbToSchSelection; // Recursion guard when synchronizing selection from PCB
// Cross-probe flashing support
wxTimer m_crossProbeFlashTimer; ///< Timer to toggle selection visibility
int m_crossProbeFlashPhase = 0;
std::vector<KIID> m_crossProbeFlashItems; ///< Items to flash
bool m_crossProbeFlashing = false;
bool m_show_search;
bool m_highlightedConnChanged;

View File

@ -34,6 +34,7 @@ struct KICOMMON_API CROSS_PROBING_SETTINGS
bool center_on_items; ///< Automatically pan to cross-probed items.
bool zoom_to_fit; ///< Zoom to fit items (ignored if center_on_items is off).
bool auto_highlight; ///< Automatically turn on highlight mode in the target frame.
bool flash_selection; ///< Flash newly cross-probed selection (visual attention aid).
};
namespace KIGFX

View File

@ -645,6 +645,25 @@ void PCB_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail )
Update3DView( false, GetPcbNewSettings()->m_Display.m_Live3DRefresh );
m_probingSchToPcb = false;
if( GetPcbNewSettings()->m_CrossProbing.flash_selection )
{
wxLogTrace( "CROSS_PROBE_FLASH", "MAIL_SELECTION(_FORCE) PCB: flash enabled, items=%zu", items.size() );
if( items.empty() )
{
wxLogTrace( "CROSS_PROBE_FLASH", "MAIL_SELECTION(_FORCE) PCB: nothing to flash" );
}
else
{
std::vector<BOARD_ITEM*> boardItems;
std::copy( items.begin(), items.end(), std::back_inserter( boardItems ) );
StartCrossProbeFlash( boardItems );
}
}
else
{
wxLogTrace( "CROSS_PROBE_FLASH", "MAIL_SELECTION(_FORCE) PCB: flash disabled" );
}
}
break;

View File

@ -222,6 +222,7 @@ void PANEL_DISPLAY_OPTIONS::loadPCBSettings( PCBNEW_SETTINGS* aCfg )
m_checkCrossProbeCenter->SetValue( aCfg->m_CrossProbing.center_on_items );
m_checkCrossProbeZoom->SetValue( aCfg->m_CrossProbing.zoom_to_fit );
m_checkCrossProbeAutoHighlight->SetValue( aCfg->m_CrossProbing.auto_highlight );
m_checkCrossProbeFlash->SetValue( aCfg->m_CrossProbing.flash_selection );
}
@ -353,6 +354,7 @@ bool PANEL_DISPLAY_OPTIONS::TransferDataFromWindow()
cfg->m_CrossProbing.center_on_items = m_checkCrossProbeCenter->GetValue();
cfg->m_CrossProbing.zoom_to_fit = m_checkCrossProbeZoom->GetValue();
cfg->m_CrossProbing.auto_highlight = m_checkCrossProbeAutoHighlight->GetValue();
cfg->m_CrossProbing.flash_selection = m_checkCrossProbeFlash->GetValue();
}
}
else

View File

@ -244,6 +244,10 @@ PANEL_DISPLAY_OPTIONS_BASE::PANEL_DISPLAY_OPTIONS_BASE( wxWindow* parent, wxWind
bSizer8->Add( m_checkCrossProbeAutoHighlight, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 );
m_checkCrossProbeFlash = new wxCheckBox( pcbPage, wxID_ANY, _("Flash cross-probed selection"), wxDefaultPosition, wxDefaultSize, 0 );
m_checkCrossProbeFlash->SetToolTip( _("Temporarily flash the newly cross-probed selection 3 times") );
bSizer8->Add( m_checkCrossProbeFlash, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 );
m_live3Drefresh = new wxCheckBox( pcbPage, wxID_ANY, _("Refresh 3D view automatically"), wxDefaultPosition, wxDefaultSize, 0 );
m_live3Drefresh->SetToolTip( _("When enabled, edits to the board will cause the 3D view to refresh (may be slow with larger boards)") );

View File

@ -2135,6 +2135,71 @@
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxBOTTOM|wxLEFT|wxRIGHT</property>
<property name="proportion">0</property>
<object class="wxCheckBox" expanded="false">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer">0</property>
<property name="aui_name"></property>
<property name="aui_position">0</property>
<property name="aui_row">0</property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="checked">1</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Flash cross-probed selection</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_checkCrossProbeFlash</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip">Temporarily flash the newly cross-probed selection 3 times</property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxBOTTOM|wxLEFT|wxRIGHT</property>

View File

@ -72,6 +72,7 @@ class PANEL_DISPLAY_OPTIONS_BASE : public RESETTABLE_PANEL
wxCheckBox* m_checkCrossProbeCenter;
wxCheckBox* m_checkCrossProbeZoom;
wxCheckBox* m_checkCrossProbeAutoHighlight;
wxCheckBox* m_checkCrossProbeFlash;
wxCheckBox* m_live3Drefresh;
// Virtual event handlers, override them in your derived class

View File

@ -115,6 +115,7 @@
#include <footprint_viewer_frame.h>
#include <footprint_chooser_frame.h>
#include <toolbars_pcb_editor.h>
#include <wx/log.h>
#ifdef KICAD_IPC_API
#include <api/api_server.h>
@ -198,6 +199,9 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
m_probingSchToPcb = false;
m_show_search = false;
m_show_net_inspector = false;
// Ensure timer has an owner before binding so it generates events.
m_crossProbeFlashTimer.SetOwner( this );
Bind( wxEVT_TIMER, &PCB_EDIT_FRAME::OnCrossProbeFlashTimer, this, m_crossProbeFlashTimer.GetId() );
// We don't know what state board was in when it was last saved, so we have to
// assume dirty
@ -569,6 +573,100 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
Bind( EDA_EVT_CLOSE_DIALOG_BOOK_REPORTER, &PCB_EDIT_FRAME::onCloseModelessBookReporterDialogs, this );
}
void PCB_EDIT_FRAME::StartCrossProbeFlash( const std::vector<BOARD_ITEM*>& aItems )
{
if( !GetPcbNewSettings()->m_CrossProbing.flash_selection )
{
wxLogTrace( "CROSS_PROBE_FLASH", "StartCrossProbeFlash(PCB): aborted (setting disabled) items=%zu", aItems.size() );
return;
}
if( aItems.empty() )
{
wxLogTrace( "CROSS_PROBE_FLASH", "StartCrossProbeFlash(PCB): aborted (no items)" );
return;
}
if( m_crossProbeFlashing )
{
wxLogTrace( "CROSS_PROBE_FLASH", "StartCrossProbeFlash(PCB): restarting existing flash (phase=%d)" , m_crossProbeFlashPhase );
m_crossProbeFlashTimer.Stop();
}
wxLogTrace( "CROSS_PROBE_FLASH", "StartCrossProbeFlash(PCB): starting with %zu items", aItems.size() );
// Store uuids
m_crossProbeFlashItems.clear();
for( BOARD_ITEM* it : aItems )
m_crossProbeFlashItems.push_back( it->m_Uuid );
m_crossProbeFlashPhase = 0;
m_crossProbeFlashing = true;
if( !m_crossProbeFlashTimer.GetOwner() )
m_crossProbeFlashTimer.SetOwner( this );
bool started = m_crossProbeFlashTimer.Start( 500, wxTIMER_CONTINUOUS ); // 0.5s intervals -> 3s total for 6 phases
wxLogTrace( "CROSS_PROBE_FLASH", "StartCrossProbeFlash(PCB): timer start=%d id=%d", (int) started, m_crossProbeFlashTimer.GetId() );
}
void PCB_EDIT_FRAME::OnCrossProbeFlashTimer( wxTimerEvent& aEvent )
{
wxLogTrace( "CROSS_PROBE_FLASH", "Timer(PCB) fired: phase=%d running=%d items=%zu", m_crossProbeFlashPhase, (int) m_crossProbeFlashing, m_crossProbeFlashItems.size() );
if( !m_crossProbeFlashing )
{
wxLogTrace( "CROSS_PROBE_FLASH", "Timer(PCB) fired but not flashing (ignored)" );
return;
}
PCB_SELECTION_TOOL* selTool = GetToolManager()->GetTool<PCB_SELECTION_TOOL>();
if( !selTool )
return;
// Prevent recursion / IPC during flashing
bool prevGuard = m_probingSchToPcb;
m_probingSchToPcb = true;
if( m_crossProbeFlashPhase % 2 == 0 )
{
// Hide selection
selTool->ClearSelection( true );
wxLogTrace( "CROSS_PROBE_FLASH", "Phase %d (PCB): cleared selection", m_crossProbeFlashPhase );
}
else
{
// Restore selection
for( const KIID& id : m_crossProbeFlashItems )
{
if( EDA_ITEM* item = GetBoard()->ResolveItem( id, true ) )
selTool->AddItemToSel( item, true );
}
wxLogTrace( "CROSS_PROBE_FLASH", "Phase %d (PCB): restored %zu items", m_crossProbeFlashPhase, m_crossProbeFlashItems.size() );
}
// Force a redraw even if the canvas / frame does not currently have focus (mouse elsewhere)
if( GetCanvas() )
{
GetCanvas()->ForceRefresh();
wxLogTrace( "CROSS_PROBE_FLASH", "Phase %d (PCB): forced canvas refresh", m_crossProbeFlashPhase );
}
m_probingSchToPcb = prevGuard;
m_crossProbeFlashPhase++;
if( m_crossProbeFlashPhase > 6 )
{
// Ensure final state (selected)
for( const KIID& id : m_crossProbeFlashItems )
{
if( EDA_ITEM* item = GetBoard()->ResolveItem( id, true ) )
selTool->AddItemToSel( item, true );
}
m_crossProbeFlashing = false;
m_crossProbeFlashTimer.Stop();
wxLogTrace( "CROSS_PROBE_FLASH", "Flashing complete (PCB). Final selection size=%zu", m_crossProbeFlashItems.size() );
}
}
PCB_EDIT_FRAME::~PCB_EDIT_FRAME()
{

View File

@ -823,6 +823,15 @@ public:
bool m_probingSchToPcb; // Recursion guard when synchronizing selection from schematic
// Cross-probe flashing support
wxTimer m_crossProbeFlashTimer; ///< Timer to toggle selection visibility for flash
int m_crossProbeFlashPhase = 0; ///< Phase counter
std::vector<KIID> m_crossProbeFlashItems; ///< Items to flash (by UUID)
bool m_crossProbeFlashing = false; ///< Currently flashing guard
void StartCrossProbeFlash( const std::vector<BOARD_ITEM*>& aItems );
void OnCrossProbeFlashTimer( wxTimerEvent& aEvent );
private:
friend struct PCB::IFACE;
friend class APPEARANCE_CONTROLS;