Restoring the previous performance of COMPONENT_TREE widget

It turns out the fastest way to update wxDataViewCtrl is to nuke the
model and rebuild it from scratch rather than via
ItemAdded()/ItemDeleted() update notifications.

Fixes: lp:1734773
* https://bugs.launchpad.net/kicad/+bug/1734773
This commit is contained in:
Maciej Suminski 2017-11-30 12:31:36 +01:00
parent a240f88785
commit 45bf919923
13 changed files with 141 additions and 203 deletions

View File

@ -105,7 +105,6 @@ int CMP_TREE_NODE::Compare( CMP_TREE_NODE const& aNode1, CMP_TREE_NODE const& aN
CMP_TREE_NODE::CMP_TREE_NODE()
: Parent( nullptr ),
Type( INVALID ),
InTree( false ),
IntrinsicRank( 0 ),
Score( kLowestDefaultScore ),
Unit( 0 )
@ -178,7 +177,6 @@ CMP_TREE_NODE_LIB_ID::CMP_TREE_NODE_LIB_ID( CMP_TREE_NODE* aParent, LIB_ALIAS* a
CMP_TREE_NODE_UNIT& CMP_TREE_NODE_LIB_ID::AddUnit( int aUnit )
{
CMP_TREE_NODE_UNIT* unit = new CMP_TREE_NODE_UNIT( this, aUnit );
unit->InTree = true;
Children.push_back( std::unique_ptr<CMP_TREE_NODE>( unit ) );
return *unit;
}
@ -245,7 +243,6 @@ CMP_TREE_NODE_LIB::CMP_TREE_NODE_LIB( CMP_TREE_NODE* aParent, wxString const& aN
CMP_TREE_NODE_LIB_ID& CMP_TREE_NODE_LIB::AddAlias( LIB_ALIAS* aAlias )
{
CMP_TREE_NODE_LIB_ID* alias = new CMP_TREE_NODE_LIB_ID( this, aAlias );
alias->InTree = true;
Children.push_back( std::unique_ptr<CMP_TREE_NODE>( alias ) );
return *alias;
}
@ -272,7 +269,6 @@ CMP_TREE_NODE_ROOT::CMP_TREE_NODE_ROOT()
CMP_TREE_NODE_LIB& CMP_TREE_NODE_ROOT::AddLib( wxString const& aName )
{
CMP_TREE_NODE_LIB* lib = new CMP_TREE_NODE_LIB( this, aName );
lib->InTree = true;
Children.push_back( std::unique_ptr<CMP_TREE_NODE>( lib ) );
return *lib;
}

View File

@ -85,7 +85,6 @@ public:
CMP_TREE_NODE* Parent; ///< Parent node or null
PTR_VECTOR Children; ///< List of child nodes
enum TYPE Type; ///< Node type
bool InTree; ///< Flag indicating whether the node is added to the tree
/**
* The rank of the item before any search terms are applied. This is

View File

@ -25,6 +25,7 @@
#include <wx/progdlg.h>
#include <wx/tokenzr.h>
#include <wx/wupdlock.h>
CMP_TREE_MODEL_ADAPTER_BASE::WIDTH_CACHE CMP_TREE_MODEL_ADAPTER_BASE::m_width_cache;
@ -61,7 +62,7 @@ unsigned int CMP_TREE_MODEL_ADAPTER_BASE::IntoArray(
for( auto const& child: aNode.Children )
{
if( child->Score > 0 && child->InTree )
if( child->Score > 0 )
{
aChildren.Add( ToItem( &*child ) );
++n;
@ -144,7 +145,7 @@ void CMP_TREE_MODEL_ADAPTER_BASE::AddAliasList(
void CMP_TREE_MODEL_ADAPTER_BASE::UpdateSearchString( wxString const& aSearch )
{
m_widget->Freeze();
wxWindowUpdateLocker updateLock( m_widget );
m_tree.ResetScore();
wxStringTokenizer tokenizer( aSearch );
@ -157,16 +158,23 @@ void CMP_TREE_MODEL_ADAPTER_BASE::UpdateSearchString( wxString const& aSearch )
m_tree.UpdateScore( matcher );
}
filterContents();
m_tree.SortNodes();
Cleared();
#ifndef __WINDOWS__
// The fastest method to update wxDataViewCtrl is to rebuild from scratch by calling Cleared().
// Linux requires to reassociate model to display data, but Windows will create multiple
// associations.
AttachTo( m_widget );
#endif
ShowResults() || ShowPreselect() || ShowSingleLibrary();
m_widget->Thaw();
}
void CMP_TREE_MODEL_ADAPTER_BASE::AttachTo( wxDataViewCtrl* aDataViewCtrl )
{
wxWindowUpdateLocker updateLock( aDataViewCtrl );
m_widget = aDataViewCtrl;
aDataViewCtrl->Freeze();
aDataViewCtrl->SetIndent( kDataViewIndent );
aDataViewCtrl->AssociateModel( this );
aDataViewCtrl->ClearColumns();
@ -179,7 +187,6 @@ void CMP_TREE_MODEL_ADAPTER_BASE::AttachTo( wxDataViewCtrl* aDataViewCtrl )
m_col_desc = aDataViewCtrl->AppendTextColumn( desc_head, 1, wxDATAVIEW_CELL_INERT,
ColWidth( m_tree, 1, desc_head ) );
m_col_part->SetSortOrder( 0 );
aDataViewCtrl->Thaw();
}
@ -464,53 +471,3 @@ bool CMP_TREE_MODEL_ADAPTER_BASE::ShowSingleLibrary()
n->Parent->Parent->Children.size() == 1;
} );
}
void CMP_TREE_MODEL_ADAPTER_BASE::filterContents()
{
// Rebuild the tree using only the filtered nodes
for( auto& lib : m_tree.Children )
{
lib->InTree = false;
for( auto& alias : lib->Children )
{
alias->InTree = false;
for( auto& unit : lib->Children )
unit->InTree = false;
}
}
m_tree.SortNodes();
Cleared();
for( auto& lib : m_tree.Children )
{
if( lib->Score <= 0 )
continue;
wxDataViewItem libItem = ToItem( lib.get() );
lib->InTree = true;
ItemAdded( wxDataViewItem( nullptr ), libItem );
for( auto& alias : lib->Children )
{
if( alias->Score > 0 )
{
alias->InTree = true;
wxDataViewItem aliasItem = ToItem( alias.get() );
ItemAdded( libItem, aliasItem );
if( !m_show_units )
continue;
for( auto& unit : alias->Children )
{
unit->InTree = true;
ItemAdded( aliasItem, ToItem( unit.get() ) );
}
}
}
}
}

View File

@ -395,11 +395,6 @@ private:
* Find and expand a library if there is only one
*/
bool ShowSingleLibrary();
/**
* Filters the items shown in the view according to the score.
*/
void filterContents();
};
#endif // _CMP_TREE_MODEL_ADAPTER_BASE_H

View File

@ -358,7 +358,7 @@ bool LIB_MANAGER::UpdatePart( LIB_PART* aPart, const wxString& aLibrary, wxStrin
screen->SetModify();
}
getAdapter()->UpdateLibrary( aLibrary );
m_frame.SyncLibraries( false );
return true;
}
@ -401,7 +401,7 @@ bool LIB_MANAGER::RevertLibrary( const wxString& aLibrary )
return false;
m_libs.erase( it );
getAdapter()->UpdateLibrary( aLibrary );
m_frame.SyncLibraries( false );
return true;
}
@ -414,7 +414,7 @@ bool LIB_MANAGER::RemovePart( const wxString& aAlias, const wxString& aLibrary )
wxCHECK( partBuf, false );
bool res = libBuf.DeleteBuffer( partBuf );
getAdapter()->UpdateLibrary( aLibrary );
m_frame.SyncLibraries( false );
return res;
}
@ -549,7 +549,7 @@ bool LIB_MANAGER::addLibrary( const wxString& aFilePath, bool aCreate, SYMBOL_LI
aTable->CreateSymbolLib( libName );
}
getAdapter()->AddLibrary( libName );
m_frame.SyncLibraries( false );
return true;
}

View File

@ -37,32 +37,6 @@ CMP_TREE_MODEL_ADAPTER_BASE::PTR LIB_MANAGER_ADAPTER::Create( LIB_MANAGER* aLibM
void LIB_MANAGER_ADAPTER::AddLibrary( const wxString& aLibNickname )
{
auto& lib_node = m_tree.AddLib( aLibNickname );
ItemAdded( wxDataViewItem( nullptr ), ToItem( &lib_node ) );
updateLibrary( lib_node );
finishUpdate();
}
void LIB_MANAGER_ADAPTER::RemoveLibrary( const wxString& aLibNickname )
{
auto it = std::find_if( m_tree.Children.begin(), m_tree.Children.end(),
[&] ( std::unique_ptr<CMP_TREE_NODE>& node ) { return node->Name == aLibNickname; } );
if( it != m_tree.Children.end() )
deleteLibrary( it );
}
void LIB_MANAGER_ADAPTER::UpdateLibrary( const wxString& aLibraryName )
{
CMP_TREE_NODE* node = findLibrary( aLibraryName );
if( !node )
return;
updateLibrary( *(CMP_TREE_NODE_LIB*) node );
finishUpdate();
}
@ -115,7 +89,9 @@ void LIB_MANAGER_ADAPTER::Sync( bool aForce, std::function<void(int, int, const
if( m_libHashes.count( libName ) == 0 )
{
aProgressCallback( i++, max, libName );
AddLibrary( libName );
auto& lib_node = m_tree.AddLib( libName );
updateLibrary( lib_node );
m_tree.AssignIntrinsicRanks();
}
}
}
@ -140,22 +116,15 @@ void LIB_MANAGER_ADAPTER::updateLibrary( CMP_TREE_NODE_LIB& aLibNode )
if( m_libHashes.count( aLibNode.Name ) == 0 )
{
// add a new library
wxDataViewItem parent = ToItem( &aLibNode );
for( auto alias : m_libMgr->GetAliases( aLibNode.Name ) )
{
auto& aliasNode = aLibNode.AddAlias( alias );
ItemAdded( parent, ToItem( &aliasNode ) );
}
aLibNode.AddAlias( alias );
}
else
{
// update an existing libary
std::list<LIB_ALIAS*> aliases = m_libMgr->GetAliases( aLibNode.Name );
wxDataViewItem parent = ToItem( &aLibNode );
// remove the common part from the aliases list
//for( const auto& node : aLibNode.Children )
for( auto nodeIt = aLibNode.Children.begin(); nodeIt != aLibNode.Children.end(); /**/ )
{
auto aliasIt = std::find_if( aliases.begin(), aliases.end(),
@ -173,17 +142,13 @@ void LIB_MANAGER_ADAPTER::updateLibrary( CMP_TREE_NODE_LIB& aLibNode )
else
{
// node does not exist in the library manager, remove the corresponding node
ItemDeleted( parent, ToItem( nodeIt->get() ) );
nodeIt = aLibNode.Children.erase( nodeIt );
}
}
// now the aliases list contains only new aliases that need to be added to the tree
for( auto alias : aliases )
{
auto& aliasNode = aLibNode.AddAlias( alias );
ItemAdded( parent, ToItem( &aliasNode ) );
}
aLibNode.AddAlias( alias );
}
aLibNode.AssignIntrinsicRanks();
@ -195,19 +160,12 @@ CMP_TREE_NODE::PTR_VECTOR::iterator LIB_MANAGER_ADAPTER::deleteLibrary(
CMP_TREE_NODE::PTR_VECTOR::iterator& aLibNodeIt )
{
CMP_TREE_NODE* node = aLibNodeIt->get();
ItemDeleted( wxDataViewItem( nullptr ), ToItem( node ) );
m_libHashes.erase( node->Name );
auto it = m_tree.Children.erase( aLibNodeIt );
return it;
}
void LIB_MANAGER_ADAPTER::finishUpdate()
{
m_tree.AssignIntrinsicRanks();
}
CMP_TREE_NODE* LIB_MANAGER_ADAPTER::findLibrary( const wxString& aLibNickName )
{
for( auto& lib : m_tree.Children )

View File

@ -37,14 +37,10 @@ public:
void AddLibrary( const wxString& aLibNickname ) override;
void RemoveLibrary( const wxString& aLibNickname );
void AddAliasList( const wxString& aNodeName, const wxArrayString& aAliasNameList ) override;
bool IsContainer( const wxDataViewItem& aItem ) const override;
void UpdateLibrary( const wxString& aLibraryName );
void Sync( bool aForce = false, std::function<void(int, int, const wxString&)> aProgressCallback
= [](int, int, const wxString&){} );
@ -56,8 +52,6 @@ protected:
CMP_TREE_NODE::PTR_VECTOR::iterator deleteLibrary(
CMP_TREE_NODE::PTR_VECTOR::iterator& aLibNodeIt );
void finishUpdate();
CMP_TREE_NODE* findLibrary( const wxString& aLibNickName );
bool GetAttr( wxDataViewItem const& aItem, unsigned int aCol,

View File

@ -253,7 +253,7 @@ LIB_EDIT_FRAME::LIB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
m_canvas->SetEnableBlockCommands( true );
m_libMgr = new LIB_MANAGER( *this );
SyncLibraries();
SyncLibraries( true );
m_treePane = new CMP_TREE_PANE( this, m_libMgr );
ReCreateMenuBar();
@ -485,7 +485,7 @@ void LIB_EDIT_FRAME::OnToggleSearchTree( wxCommandEvent& event )
void LIB_EDIT_FRAME::OnEditSymbolLibTable( wxCommandEvent& aEvent )
{
SCH_BASE_FRAME::OnEditSymbolLibTable( aEvent );
SyncLibraries();
SyncLibraries( true );
}
@ -1614,23 +1614,24 @@ wxString LIB_EDIT_FRAME::getTargetLib() const
}
void LIB_EDIT_FRAME::SyncLibraries()
void LIB_EDIT_FRAME::SyncLibraries( bool aProgress )
{
auto tree = m_treePane ? m_treePane->GetCmpTree() : nullptr;
wxBusyCursor cursor;
if( aProgress )
{
wxProgressDialog progressDlg( _( "Loading symbol libraries" ),
wxEmptyString, m_libMgr->GetAdapter()->GetLibrariesCount(), this );
if( tree )
tree->Freeze();
m_libMgr->Sync( true, [&]( int progress, int max, const wxString& libName ) {
progressDlg.Update( progress, wxString::Format( _( "Loading library '%s'" ), libName ) );
} );
}
else
{
m_libMgr->Sync( true );
}
wxProgressDialog progressDlg( _( "Loading symbol libraries" ),
wxEmptyString, m_libMgr->GetAdapter()->GetLibrariesCount(), this );
m_libMgr->Sync( true, [&]( int progress, int max, const wxString& libName ) {
progressDlg.Update( progress, wxString::Format( _( "Loading library '%s'" ), libName ) );
} );
if( tree )
tree->Thaw();
if( m_treePane )
m_treePane->Regenerate();
}

View File

@ -689,7 +689,7 @@ public:
/**
* Synchronize the library manager and the symbol library table. Displays a progress dialog.
*/
void SyncLibraries();
void SyncLibraries( bool aLoad );
private:
///> Helper screen used when no part is loaded

View File

@ -30,20 +30,18 @@
#include <libeditframe.h>
#include <symbol_lib_table.h>
CMP_TREE_PANE::CMP_TREE_PANE( LIB_EDIT_FRAME* aParent, LIB_MANAGER* aLibMgr )
: wxPanel( aParent ),
m_libEditFrame( aParent ),
m_libMgr( aLibMgr )
m_libEditFrame( aParent ), m_tree( nullptr ), m_libMgr( aLibMgr )
{
// Create widgets
wxBoxSizer* boxSizer = new wxBoxSizer( wxVERTICAL );
m_tree = new COMPONENT_TREE( this, &SYMBOL_LIB_TABLE::GetGlobalLibTable(),
m_libMgr->GetAdapter(), COMPONENT_TREE::SEARCH );
boxSizer->Add( m_tree, 1, wxEXPAND | wxALL, 5 );
SetSizer( boxSizer );
SetSizer( boxSizer ); // should remove the previous sizer according to wxWidgets docs
Layout();
boxSizer->Fit( this );
@ -87,7 +85,14 @@ CMP_TREE_PANE::CMP_TREE_PANE( LIB_EDIT_FRAME* aParent, LIB_MANAGER* aLibMgr )
CMP_TREE_PANE::~CMP_TREE_PANE()
{
delete m_tree;
m_tree->Destroy();
}
void CMP_TREE_PANE::Regenerate()
{
if( m_tree )
m_tree->Regenerate();
}

View File

@ -26,10 +26,13 @@
#define CMP_TREE_PANE_H
#include <wx/panel.h>
#include <wx/dataview.h>
#include <vector>
class COMPONENT_TREE;
class LIB_EDIT_FRAME;
class LIB_MANAGER;
class wxBoxSizer;
/**
* Library Editor pane with component tree and symbol library table selector.
@ -45,6 +48,9 @@ public:
return m_tree;
}
///> Updates the component tree
void Regenerate();
protected:
void onComponentSelected( wxCommandEvent& aEvent );

View File

@ -31,6 +31,7 @@
#include <wx/sizer.h>
#include <wx/statbmp.h>
#include <wx/html/htmlwin.h>
#include <wx/wupdlock.h>
#include <symbol_lib_table.h>
@ -138,6 +139,25 @@ void COMPONENT_TREE::SelectLibId( const LIB_ID& aLibId )
}
void COMPONENT_TREE::Regenerate()
{
STATE current;
// Store the state
if( !m_filtering )
m_unfilteredState = getState();
else
current = getState();
m_adapter->UpdateSearchString( m_query_ctrl->GetLineText( 0 ) );
m_filtering = !m_query_ctrl->IsEmpty();
postPreselectEvent();
// Restore the state
setState( m_filtering ? current : m_unfilteredState );
}
void COMPONENT_TREE::selectIfValid( const wxDataViewItem& aTreeId )
{
if( aTreeId.IsOk() )
@ -163,25 +183,42 @@ void COMPONENT_TREE::postSelectEvent()
}
COMPONENT_TREE::STATE COMPONENT_TREE::getState() const
{
STATE state;
wxDataViewItemArray items;
m_adapter->GetChildren( wxDataViewItem( nullptr ), items );
for( const auto& item : items )
{
if( m_tree_ctrl->IsExpanded( item ) )
state.expanded.push_back( item );
}
state.selection = m_tree_ctrl->GetSelection();
return state;
}
void COMPONENT_TREE::setState( const STATE& aState )
{
wxWindowUpdateLocker updateLock( m_tree_ctrl );
for( const auto& item : aState.expanded )
m_tree_ctrl->Expand( item );
if( aState.selection.IsOk() )
{
m_tree_ctrl->ExpandAncestors( aState.selection );
m_tree_ctrl->SetCurrentItem( aState.selection );
}
}
void COMPONENT_TREE::onQueryText( wxCommandEvent& aEvent )
{
// Store the state
if( !m_filtering )
{
m_selection = m_tree_ctrl->GetSelection();
saveExpandFlag();
}
m_adapter->UpdateSearchString( m_query_ctrl->GetLineText( 0 ) );
m_filtering = !m_query_ctrl->IsEmpty();
postPreselectEvent();
// Restore the state
if( !m_filtering )
{
selectIfValid( m_selection );
restoreExpandFlag();
}
Regenerate();
// Required to avoid interaction with SetHint()
// See documentation for wxTextEntry::SetHint
@ -276,32 +313,5 @@ void COMPONENT_TREE::onContextMenu( wxDataViewEvent& aEvent )
}
void COMPONENT_TREE::saveExpandFlag()
{
wxDataViewItemArray items;
m_adapter->GetChildren( wxDataViewItem( nullptr ), items );
m_expanded.clear();
for( const auto& item : items )
{
if( m_tree_ctrl->IsExpanded( item ) )
m_expanded.push_back( item );
}
}
void COMPONENT_TREE::restoreExpandFlag()
{
m_tree_ctrl->Freeze();
for( const auto& item : m_expanded )
{
m_tree_ctrl->Expand( item );
}
m_tree_ctrl->Thaw();
}
wxDEFINE_EVENT( COMPONENT_PRESELECTED, wxCommandEvent );
wxDEFINE_EVENT( COMPONENT_SELECTED, wxCommandEvent );

View File

@ -86,6 +86,11 @@ public:
return m_menuActive;
}
/**
* Regenerates the tree.
*/
void Regenerate();
protected:
/**
* If a wxDataViewitem is valid, select it and post a selection event.
@ -103,6 +108,28 @@ protected:
*/
void postSelectEvent();
/**
* Structure storing state of the component tree widget.
*/
struct STATE
{
///> List of expanded nodes
std::vector<wxDataViewItem> expanded;
///> Current selection, might be not valid if nothing was selected
wxDataViewItem selection;
};
/**
* Returns the component tree widget state.
*/
STATE getState() const;
/**
* Restores the component tree widget state from an object.
*/
void setState( const STATE& aState );
void onQueryText( wxCommandEvent& aEvent );
void onQueryEnter( wxCommandEvent& aEvent );
void onQueryCharHook( wxKeyEvent& aEvent );
@ -114,16 +141,6 @@ protected:
void onPreselect( wxCommandEvent& aEvent );
void onContextMenu( wxDataViewEvent& aEvent );
/**
* Store the list of expanded nodes in the tree widget.
*/
void saveExpandFlag();
/**
* Restore the expanded nodes in the tree widget.
*/
void restoreExpandFlag();
SYMBOL_LIB_TABLE* m_sym_lib_table;
CMP_TREE_MODEL_ADAPTER_BASE::PTR m_adapter;
@ -137,11 +154,11 @@ protected:
///> Flag indicating whether a right-click context menu is active
bool m_menuActive;
///> Flag indicating whether the results are filtered using the search query
bool m_filtering;
///> List of expanded nodes
std::vector<wxDataViewItem> m_expanded;
wxDataViewItem m_selection;
///> State of the widget before any filters applied
STATE m_unfilteredState;
};
///> Custom event sent when a new component is preselected