Experiment with loading recent searches into the search menu.

Fixes https://gitlab.com/kicad/code/kicad/issues/11743
This commit is contained in:
Jeff Young 2022-09-15 14:32:27 +01:00
parent 83fb06ff75
commit a21d24a4c8
6 changed files with 83 additions and 12 deletions

View File

@ -23,6 +23,7 @@
*/ */
#include <widgets/lib_tree.h> #include <widgets/lib_tree.h>
#include <core/kicad_algo.h>
#include <macros.h> #include <macros.h>
#include <wxdataviewctrl_helpers.h> #include <wxdataviewctrl_helpers.h>
#include <wx/sizer.h> #include <wx/sizer.h>
@ -33,13 +34,20 @@
#include <wx/timer.h> #include <wx/timer.h>
LIB_TREE::LIB_TREE( wxWindow* aParent, LIB_TABLE* aLibTable, constexpr int RECENT_SEARCHES_MAX = 10;
wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>& aAdapter, FLAGS aFlags,
std::map<wxString, std::vector<wxString>> g_recentSearches;
LIB_TREE::LIB_TREE( wxWindow* aParent, const wxString& aRecentSearchesKey, LIB_TABLE* aLibTable,
wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>& aAdapter, int aFlags,
HTML_WINDOW* aDetails ) : HTML_WINDOW* aDetails ) :
wxPanel( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxPanel( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
wxWANTS_CHARS | wxTAB_TRAVERSAL | wxNO_BORDER ), wxWANTS_CHARS | wxTAB_TRAVERSAL | wxNO_BORDER ),
m_lib_table( aLibTable ), m_adapter( aAdapter ), m_query_ctrl( nullptr ), m_lib_table( aLibTable ), m_adapter( aAdapter ), m_query_ctrl( nullptr ),
m_details_ctrl( nullptr ) m_details_ctrl( nullptr ),
m_inTimerEvent( false ),
m_recentSearchesKey( aRecentSearchesKey )
{ {
wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL ); wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
@ -76,6 +84,17 @@ LIB_TREE::LIB_TREE( wxWindow* aParent, LIB_TABLE* aLibTable,
m_query_ctrl->Bind( wxEVT_CHAR_HOOK, &LIB_TREE::onQueryCharHook, this ); m_query_ctrl->Bind( wxEVT_CHAR_HOOK, &LIB_TREE::onQueryCharHook, this );
m_query_ctrl->Bind( wxEVT_MOTION, &LIB_TREE::onQueryMouseMoved, this ); m_query_ctrl->Bind( wxEVT_MOTION, &LIB_TREE::onQueryMouseMoved, this );
m_query_ctrl->Bind( wxEVT_MENU,
[this]( wxCommandEvent& aEvent )
{
wxString search;
size_t idx = aEvent.GetId() - 1;
if( idx < g_recentSearches[ m_recentSearchesKey ].size() )
m_query_ctrl->SetValue( g_recentSearches[ m_recentSearchesKey ][idx] );
},
1, RECENT_SEARCHES_MAX );
Bind( wxEVT_TIMER, &LIB_TREE::onDebounceTimer, this, m_debounceTimer->GetId() ); Bind( wxEVT_TIMER, &LIB_TREE::onDebounceTimer, this, m_debounceTimer->GetId() );
} }
@ -124,6 +143,7 @@ LIB_TREE::LIB_TREE( wxWindow* aParent, LIB_TABLE* aLibTable,
m_query_ctrl->SetDescriptiveText( _( "Filter" ) ); m_query_ctrl->SetDescriptiveText( _( "Filter" ) );
m_query_ctrl->SetFocus(); m_query_ctrl->SetFocus();
m_query_ctrl->SetValue( wxEmptyString ); m_query_ctrl->SetValue( wxEmptyString );
updateRecentSearchMenu();
// Force an update of the adapter with the empty text to ensure preselect is done // Force an update of the adapter with the empty text to ensure preselect is done
Regenerate( false ); Regenerate( false );
@ -235,6 +255,35 @@ wxString LIB_TREE::GetSearchString() const
} }
void LIB_TREE::updateRecentSearchMenu()
{
wxString newEntry = GetSearchString();
std::vector<wxString>& recents = g_recentSearches[ m_recentSearchesKey ];
if( !newEntry.IsEmpty() )
{
if( alg::contains( recents, newEntry ) )
alg::delete_matching( recents, newEntry );
if( recents.size() >= RECENT_SEARCHES_MAX )
recents.pop_back();
recents.insert( recents.begin(), newEntry );
}
wxMenu* menu = new wxMenu();
for( const wxString& recent : recents )
menu->Append( menu->GetMenuItemCount() + 1, recent );
if( recents.empty() )
menu->Append( wxID_ANY, _( "recent searches" ) );
m_query_ctrl->SetMenu( menu );
}
void LIB_TREE::Regenerate( bool aKeepState ) void LIB_TREE::Regenerate( bool aKeepState )
{ {
STATE current; STATE current;
@ -406,6 +455,8 @@ void LIB_TREE::onQueryText( wxCommandEvent& aEvent )
void LIB_TREE::onQueryEnter( wxCommandEvent& aEvent ) void LIB_TREE::onQueryEnter( wxCommandEvent& aEvent )
{ {
updateRecentSearchMenu();
if( GetSelectedLibId().IsValid() ) if( GetSelectedLibId().IsValid() )
postSelectEvent(); postSelectEvent();
} }
@ -413,7 +464,9 @@ void LIB_TREE::onQueryEnter( wxCommandEvent& aEvent )
void LIB_TREE::onDebounceTimer( wxTimerEvent& aEvent ) void LIB_TREE::onDebounceTimer( wxTimerEvent& aEvent )
{ {
m_inTimerEvent = true;
Regenerate( false ); Regenerate( false );
m_inTimerEvent = false;
} }
@ -425,26 +478,34 @@ void LIB_TREE::onQueryCharHook( wxKeyEvent& aKeyStroke )
switch( aKeyStroke.GetKeyCode() ) switch( aKeyStroke.GetKeyCode() )
{ {
case WXK_UP: case WXK_UP:
updateRecentSearchMenu();
selectIfValid( GetPrevItem( *m_tree_ctrl, sel ) ); selectIfValid( GetPrevItem( *m_tree_ctrl, sel ) );
break; break;
case WXK_DOWN: case WXK_DOWN:
updateRecentSearchMenu();
selectIfValid( GetNextItem( *m_tree_ctrl, sel ) ); selectIfValid( GetNextItem( *m_tree_ctrl, sel ) );
break; break;
case WXK_ADD: case WXK_ADD:
updateRecentSearchMenu();
if( type == LIB_TREE_NODE::LIB ) if( type == LIB_TREE_NODE::LIB )
m_tree_ctrl->Expand( sel ); m_tree_ctrl->Expand( sel );
break; break;
case WXK_SUBTRACT: case WXK_SUBTRACT:
updateRecentSearchMenu();
if( type == LIB_TREE_NODE::LIB ) if( type == LIB_TREE_NODE::LIB )
m_tree_ctrl->Collapse( sel ); m_tree_ctrl->Collapse( sel );
break; break;
case WXK_RETURN: case WXK_RETURN:
updateRecentSearchMenu();
if( type == LIB_TREE_NODE::LIB ) if( type == LIB_TREE_NODE::LIB )
toggleExpand( sel ); toggleExpand( sel );
else else
@ -476,6 +537,9 @@ void LIB_TREE::onQueryMouseMoved( wxMouseEvent& aEvent )
void LIB_TREE::onTreeSelect( wxDataViewEvent& aEvent ) void LIB_TREE::onTreeSelect( wxDataViewEvent& aEvent )
{ {
if( !m_inTimerEvent )
updateRecentSearchMenu();
if( !m_tree_ctrl->IsFrozen() ) if( !m_tree_ctrl->IsFrozen() )
postPreselectEvent(); postPreselectEvent();
} }

View File

@ -126,8 +126,9 @@ DIALOG_CHOOSE_SYMBOL::DIALOG_CHOOSE_SYMBOL( SCH_BASE_FRAME* aParent, const wxStr
wxBoxSizer* treeSizer = new wxBoxSizer( wxVERTICAL ); wxBoxSizer* treeSizer = new wxBoxSizer( wxVERTICAL );
treePanel->SetSizer( treeSizer ); treePanel->SetSizer( treeSizer );
m_tree = new LIB_TREE( treePanel, Prj().SchSymbolLibTable(), aAdapter, m_tree = new LIB_TREE( treePanel, m_showPower ? wxT( "power" ) : wxT( "symbols" ),
LIB_TREE::FLAGS::ALL_WIDGETS, m_details ); Prj().SchSymbolLibTable(), aAdapter, LIB_TREE::FLAGS::ALL_WIDGETS,
m_details );
treeSizer->Add( m_tree, 1, wxEXPAND | wxALL, 5 ); treeSizer->Add( m_tree, 1, wxEXPAND | wxALL, 5 );
treePanel->Layout(); treePanel->Layout();

View File

@ -39,9 +39,8 @@ SYMBOL_TREE_PANE::SYMBOL_TREE_PANE( SYMBOL_EDIT_FRAME* aParent, SYMBOL_LIBRARY_M
{ {
// Create widgets // Create widgets
wxBoxSizer* boxSizer = new wxBoxSizer( wxVERTICAL ); wxBoxSizer* boxSizer = new wxBoxSizer( wxVERTICAL );
m_tree = new LIB_TREE( this, &SYMBOL_LIB_TABLE::GetGlobalLibTable(), m_libMgr->GetAdapter(), m_tree = new LIB_TREE( this, wxT( "symbols" ), &SYMBOL_LIB_TABLE::GetGlobalLibTable(),
static_cast<LIB_TREE::FLAGS>( LIB_TREE::SEARCH | m_libMgr->GetAdapter(), LIB_TREE::SEARCH | LIB_TREE::MULTISELECT );
LIB_TREE::MULTISELECT ) );
boxSizer->Add( m_tree, 1, wxEXPAND, 5 ); boxSizer->Add( m_tree, 1, wxEXPAND, 5 );
SetSizer( boxSizer ); // should remove the previous sizer according to wxWidgets docs SetSizer( boxSizer ); // should remove the previous sizer according to wxWidgets docs

View File

@ -59,14 +59,16 @@ public:
* Construct a symbol tree. * Construct a symbol tree.
* *
* @param aParent parent window containing this tree widget * @param aParent parent window containing this tree widget
* @param aRecentSearchesKey a key into a global map storing recent searches (usually "power",
* "symbols", or "footprints", but could be further differentiated)
* @param aLibTable table containing libraries and items to display * @param aLibTable table containing libraries and items to display
* @param aAdapter a LIB_TREE_MODEL_ADAPTER instance to use * @param aAdapter a LIB_TREE_MODEL_ADAPTER instance to use
* @param aFlags selection of sub-widgets to include and other options * @param aFlags selection of sub-widgets to include and other options
* @param aDetails if not null, a custom HTML_WINDOW to hold symbol details. If null this * @param aDetails if not null, a custom HTML_WINDOW to hold symbol details. If null this
* will be created inside the LIB_TREE. * will be created inside the LIB_TREE.
*/ */
LIB_TREE( wxWindow* aParent, LIB_TABLE* aLibTable, LIB_TREE( wxWindow* aParent, const wxString& aRecentSearchesKey, LIB_TABLE* aLibTable,
wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>& aAdapter, FLAGS aFlags = ALL_WIDGETS, wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>& aAdapter, int aFlags = ALL_WIDGETS,
HTML_WINDOW* aDetails = nullptr ); HTML_WINDOW* aDetails = nullptr );
~LIB_TREE() override; ~LIB_TREE() override;
@ -186,6 +188,8 @@ protected:
*/ */
void setState( const STATE& aState ); void setState( const STATE& aState );
void updateRecentSearchMenu();
void onQueryText( wxCommandEvent& aEvent ); void onQueryText( wxCommandEvent& aEvent );
void onQueryEnter( wxCommandEvent& aEvent ); void onQueryEnter( wxCommandEvent& aEvent );
void onQueryCharHook( wxKeyEvent& aEvent ); void onQueryCharHook( wxKeyEvent& aEvent );
@ -210,8 +214,10 @@ protected:
wxDataViewCtrl* m_tree_ctrl; wxDataViewCtrl* m_tree_ctrl;
HTML_WINDOW* m_details_ctrl; HTML_WINDOW* m_details_ctrl;
wxTimer* m_debounceTimer; wxTimer* m_debounceTimer;
bool m_inTimerEvent;
LIB_ID m_last_libid; LIB_ID m_last_libid;
wxString m_recentSearchesKey;
}; };
///< Custom event sent when a new symbol is preselected ///< Custom event sent when a new symbol is preselected

View File

@ -81,7 +81,7 @@ DIALOG_CHOOSE_FOOTPRINT::DIALOG_CHOOSE_FOOTPRINT( PCB_BASE_FRAME* aParent,
sizer->Add( m_vsplitter, 1, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 5 ); sizer->Add( m_vsplitter, 1, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 5 );
m_tree = new LIB_TREE( m_hsplitter, Prj().PcbFootprintLibs(), aAdapter, m_tree = new LIB_TREE( m_hsplitter, wxT( "footprints" ), Prj().PcbFootprintLibs(), aAdapter,
LIB_TREE::FLAGS::ALL_WIDGETS, details ); LIB_TREE::FLAGS::ALL_WIDGETS, details );
m_hsplitter->SetSashGravity( 0.8 ); m_hsplitter->SetSashGravity( 0.8 );

View File

@ -34,7 +34,8 @@ FOOTPRINT_TREE_PANE::FOOTPRINT_TREE_PANE( FOOTPRINT_EDIT_FRAME* aParent )
{ {
// Create widgets // Create widgets
wxBoxSizer* boxSizer = new wxBoxSizer( wxVERTICAL ); wxBoxSizer* boxSizer = new wxBoxSizer( wxVERTICAL );
m_tree = new LIB_TREE( this, &GFootprintTable, m_frame->GetLibTreeAdapter(), LIB_TREE::SEARCH ); m_tree = new LIB_TREE( this, wxT( "footprints" ), &GFootprintTable,
m_frame->GetLibTreeAdapter(), LIB_TREE::SEARCH );
boxSizer->Add( m_tree, 1, wxEXPAND, 5 ); boxSizer->Add( m_tree, 1, wxEXPAND, 5 );
SetSizer( boxSizer ); // should remove the previous sizer according to wxWidgets docs SetSizer( boxSizer ); // should remove the previous sizer according to wxWidgets docs