diff --git a/common/settings/app_settings.cpp b/common/settings/app_settings.cpp index 1ae7f19883..37687f76f8 100644 --- a/common/settings/app_settings.cpp +++ b/common/settings/app_settings.cpp @@ -280,6 +280,9 @@ APP_SETTINGS_BASE::APP_SETTINGS_BASE( const std::string& aFilename, int aSchemaV m_params.emplace_back( new PARAM( "cross_probing.auto_highlight", &m_CrossProbing.auto_highlight, true ) ); + + m_params.emplace_back( new PARAM( "cross_probing.flash_selection", + &m_CrossProbing.flash_selection, false ) ); } diff --git a/eeschema/cross-probing.cpp b/eeschema/cross-probing.cpp index 2dd45e4cf1..89db65224d 100644 --- a/eeschema/cross-probing.cpp +++ b/eeschema/cross-probing.cpp @@ -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 itemPtrs; + std::copy( items.begin(), items.end(), std::back_inserter( itemPtrs ) ); + + StartCrossProbeFlash( itemPtrs ); + } + } + else + { + wxLogTrace( "CROSS_PROBE_FLASH", "MAIL_SELECTION(_FORCE): flash disabled" ); + } } break; diff --git a/eeschema/dialogs/panel_eeschema_display_options.cpp b/eeschema/dialogs/panel_eeschema_display_options.cpp index 127b8b7144..5ba84279d3 100644 --- a/eeschema/dialogs/panel_eeschema_display_options.cpp +++ b/eeschema/dialogs/panel_eeschema_display_options.cpp @@ -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(); diff --git a/eeschema/dialogs/panel_eeschema_display_options_base.cpp b/eeschema/dialogs/panel_eeschema_display_options_base.cpp index bcc525681d..e8196ecc48 100644 --- a/eeschema/dialogs/panel_eeschema_display_options_base.cpp +++ b/eeschema/dialogs/panel_eeschema_display_options_base.cpp @@ -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 ); diff --git a/eeschema/dialogs/panel_eeschema_display_options_base.fbp b/eeschema/dialogs/panel_eeschema_display_options_base.fbp index 794bd6d8d6..f4f05f5805 100644 --- a/eeschema/dialogs/panel_eeschema_display_options_base.fbp +++ b/eeschema/dialogs/panel_eeschema_display_options_base.fbp @@ -293,6 +293,71 @@ + + 5 + wxBOTTOM|wxRIGHT|wxLEFT + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Flash cross-probed selection + + 0 + + + 0 + + 1 + m_checkCrossProbeFlash + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + Temporarily flash the newly cross-probed selection 3 times + + wxFILTER_NONE + wxDefaultValidator + + + + + + 5 diff --git a/eeschema/dialogs/panel_eeschema_display_options_base.h b/eeschema/dialogs/panel_eeschema_display_options_base.h index 2650021dba..99171979ce 100644 --- a/eeschema/dialogs/panel_eeschema_display_options_base.h +++ b/eeschema/dialogs/panel_eeschema_display_options_base.h @@ -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; diff --git a/eeschema/sch_edit_frame.cpp b/eeschema/sch_edit_frame.cpp index 73ef10d643..b988fd259d 100644 --- a/eeschema/sch_edit_frame.cpp +++ b/eeschema/sch_edit_frame.cpp @@ -104,6 +104,7 @@ #include #include #include +#include #ifdef KICAD_IPC_API #include @@ -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& 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(); + 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() { diff --git a/eeschema/sch_edit_frame.h b/eeschema/sch_edit_frame.h index f99e0ffca0..ffe661f31e 100644 --- a/eeschema/sch_edit_frame.h +++ b/eeschema/sch_edit_frame.h @@ -989,6 +989,9 @@ private: void CaptureHierarchyPaneSize(); + void StartCrossProbeFlash( const std::vector& 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 m_crossProbeFlashItems; ///< Items to flash + bool m_crossProbeFlashing = false; bool m_show_search; bool m_highlightedConnChanged; diff --git a/include/settings/app_settings.h b/include/settings/app_settings.h index cab282471b..d9055ff91f 100644 --- a/include/settings/app_settings.h +++ b/include/settings/app_settings.h @@ -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 diff --git a/pcbnew/cross-probing.cpp b/pcbnew/cross-probing.cpp index c414e5cf8d..ae2a6b0180 100644 --- a/pcbnew/cross-probing.cpp +++ b/pcbnew/cross-probing.cpp @@ -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 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; diff --git a/pcbnew/dialogs/panel_display_options.cpp b/pcbnew/dialogs/panel_display_options.cpp index 9ae4591dd3..27be30aa06 100644 --- a/pcbnew/dialogs/panel_display_options.cpp +++ b/pcbnew/dialogs/panel_display_options.cpp @@ -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 diff --git a/pcbnew/dialogs/panel_display_options_base.cpp b/pcbnew/dialogs/panel_display_options_base.cpp index 36016fc972..ec52ace56f 100644 --- a/pcbnew/dialogs/panel_display_options_base.cpp +++ b/pcbnew/dialogs/panel_display_options_base.cpp @@ -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)") ); diff --git a/pcbnew/dialogs/panel_display_options_base.fbp b/pcbnew/dialogs/panel_display_options_base.fbp index 11e97a07e1..40a57f4b94 100644 --- a/pcbnew/dialogs/panel_display_options_base.fbp +++ b/pcbnew/dialogs/panel_display_options_base.fbp @@ -2135,6 +2135,71 @@ + + 5 + wxBOTTOM|wxLEFT|wxRIGHT + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Flash cross-probed selection + + 0 + + + 0 + + 1 + m_checkCrossProbeFlash + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + Temporarily flash the newly cross-probed selection 3 times + + wxFILTER_NONE + wxDefaultValidator + + + + + + 5 wxBOTTOM|wxLEFT|wxRIGHT diff --git a/pcbnew/dialogs/panel_display_options_base.h b/pcbnew/dialogs/panel_display_options_base.h index d524937025..e8de43ced7 100644 --- a/pcbnew/dialogs/panel_display_options_base.h +++ b/pcbnew/dialogs/panel_display_options_base.h @@ -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 diff --git a/pcbnew/pcb_edit_frame.cpp b/pcbnew/pcb_edit_frame.cpp index ae3b55562d..68a7299bba 100644 --- a/pcbnew/pcb_edit_frame.cpp +++ b/pcbnew/pcb_edit_frame.cpp @@ -115,6 +115,7 @@ #include #include #include +#include #ifdef KICAD_IPC_API #include @@ -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& 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(); + 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() { diff --git a/pcbnew/pcb_edit_frame.h b/pcbnew/pcb_edit_frame.h index 4463015add..697f941a68 100644 --- a/pcbnew/pcb_edit_frame.h +++ b/pcbnew/pcb_edit_frame.h @@ -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 m_crossProbeFlashItems; ///< Items to flash (by UUID) + bool m_crossProbeFlashing = false; ///< Currently flashing guard + + void StartCrossProbeFlash( const std::vector& aItems ); + void OnCrossProbeFlashTimer( wxTimerEvent& aEvent ); + private: friend struct PCB::IFACE; friend class APPEARANCE_CONTROLS;