Store hierarchy expansion/collapse state

Defaults to fully expanded but stores the names for nodes that are
collapsed in project local settings.  Because project local settings are
generally changed by modifying views or selection filters, these should
be saved on close rather than only when the project is saved

Fixes https://gitlab.com/kicad/code/kicad/-/issues/19276
This commit is contained in:
Seth Hillbrand 2025-08-04 08:26:18 -07:00
parent b67b467483
commit e379e91081
6 changed files with 100 additions and 35 deletions

View File

@ -354,6 +354,9 @@ PROJECT_LOCAL_SETTINGS::PROJECT_LOCAL_SETTINGS( PROJECT* aProject, const wxStrin
{ "otherItems", true }
} ) );
m_params.emplace_back( new PARAM_LIST<wxString>( "schematic.hierarchy_collapsed",
&m_SchHierarchyCollapsed, {} ) );
registerMigration( 1, 2,
[&]()
{

View File

@ -156,9 +156,10 @@ void SETTINGS_MANAGER::Save()
if( dynamic_cast<COLOR_SETTINGS*>( settings.get() ) )
continue;
// Never automatically save project settings, caller should use SaveProject or UnloadProject
if( dynamic_cast<PROJECT_FILE*>( settings.get() )
|| dynamic_cast<PROJECT_LOCAL_SETTINGS*>( settings.get() ) )
// Never automatically save project file, caller should use SaveProject or UnloadProject
// We do want to save the project local settings, though because they are generally view
// settings that should persist even if the project is not saved
if( dynamic_cast<PROJECT_FILE*>( settings.get() ) )
{
continue;
}

View File

@ -246,6 +246,7 @@ void SCH_EDIT_FRAME::SaveProjectLocalSettings()
SCH_SELECTION_TOOL* selTool = GetToolManager()->GetTool<SCH_SELECTION_TOOL>();
localSettings.m_SchSelectionFilter = selTool->GetFilter();
localSettings.m_SchHierarchyCollapsed = m_hierarchy->GetCollapsedPaths();
}

View File

@ -29,6 +29,7 @@
#include <tool/tool_manager.h>
#include <tools/sch_actions.h>
#include <hierarchy_pane.h>
#include <project/project_local_settings.h>
#include <kiface_base.h>
#include <wx/object.h>
#include <wx/generic/textdlgg.h>
@ -95,6 +96,11 @@ HIERARCHY_PANE::HIERARCHY_PANE( SCH_EDIT_FRAME* aParent ) :
m_events_bound = false;
PROJECT_LOCAL_SETTINGS& localSettings = m_frame->Prj().GetLocalSettings();
for( const wxString& path : localSettings.m_SchHierarchyCollapsed )
m_collapsedPaths.insert( path );
UpdateHierarchyTree();
// Enable selection events
@ -220,32 +226,39 @@ void HIERARCHY_PANE::UpdateHierarchyTree( bool aClear )
}
SCH_SHEET_LIST hierarchy = m_frame->Schematic().Hierarchy();
std::set<SCH_SHEET_PATH> expandedNodes;
std::set<wxString> collapsedNodes = m_collapsedPaths;
std::function<void( const wxTreeItemId& )> getExpandedNodes =
std::function<void( const wxTreeItemId& )> getCollapsedNodes =
[&]( const wxTreeItemId& id )
{
wxCHECK_RET( id.IsOk(), wxT( "Invalid tree item" ) );
TREE_ITEM_DATA* itemData = static_cast<TREE_ITEM_DATA*>( m_tree->GetItemData( id ) );
if( m_tree->IsExpanded( id ) && hierarchy.HasPath( itemData->m_SheetPath.Path() ) )
expandedNodes.emplace( itemData->m_SheetPath );
if( m_tree->ItemHasChildren( id ) && !m_tree->IsExpanded( id )
&& hierarchy.HasPath( itemData->m_SheetPath.Path() ) )
{
collapsedNodes.emplace( itemData->m_SheetPath.PathAsString() );
return;
}
wxTreeItemIdValue cookie;
wxTreeItemId child = m_tree->GetFirstChild( id, cookie );
while( child.IsOk() )
{
getExpandedNodes( child );
getCollapsedNodes( child );
child = m_tree->GetNextChild( id, cookie );
}
};
// If we are clearing the tree, don't try to get expanded nodes as they
// If we are clearing the tree, don't try to get collapsed nodes as they
// might be deleted
if( !aClear && !m_tree->IsEmpty() )
getExpandedNodes( m_tree->GetRootItem() );
{
collapsedNodes.clear();
getCollapsedNodes( m_tree->GetRootItem() );
}
m_list.clear();
m_list.push_back( &m_frame->Schematic().Root() );
@ -258,35 +271,35 @@ void HIERARCHY_PANE::UpdateHierarchyTree( bool aClear )
buildHierarchyTree( &m_list, root );
UpdateHierarchySelection();
if( !expandedNodes.empty() )
{
std::function<void( const wxTreeItemId& )> expandNodes =
[&]( const wxTreeItemId& id )
m_tree->ExpandAll();
std::function<void( const wxTreeItemId& )> collapseNodes =
[&]( const wxTreeItemId& id )
{
wxCHECK_RET( id.IsOk(), wxT( "Invalid tree item" ) );
TREE_ITEM_DATA* itemData =
static_cast<TREE_ITEM_DATA*>( m_tree->GetItemData( id ) );
if( id != root &&
collapsedNodes.find( itemData->m_SheetPath.PathAsString() ) != collapsedNodes.end() )
{
wxCHECK_RET( id.IsOk(), wxT( "Invalid tree item" ) );
m_tree->Collapse( id );
return;
}
TREE_ITEM_DATA* itemData =
static_cast<TREE_ITEM_DATA*>( m_tree->GetItemData( id ) );
wxTreeItemIdValue cookie;
wxTreeItemId child = m_tree->GetFirstChild( id, cookie );
if( expandedNodes.find( itemData->m_SheetPath ) != expandedNodes.end() )
m_tree->Expand( id );
while( child.IsOk() )
{
collapseNodes( child );
child = m_tree->GetNextChild( id, cookie );
}
};
wxTreeItemIdValue cookie;
wxTreeItemId child = m_tree->GetFirstChild( id, cookie );
while( child.IsOk() )
{
expandNodes( child );
child = m_tree->GetNextChild( id, cookie );
}
};
expandNodes( m_tree->GetRootItem() );
}
else if( m_tree->ItemHasChildren( root ) )
{
m_tree->Expand( root );
}
collapseNodes( root );
m_collapsedPaths = std::move( collapsedNodes );
if( eventsWereBound )
{
@ -359,6 +372,42 @@ void HIERARCHY_PANE::UpdateLabelsHierarchyTree()
}
std::vector<wxString> HIERARCHY_PANE::GetCollapsedPaths() const
{
std::vector<wxString> collapsed;
if( m_tree->IsEmpty() )
return collapsed;
std::function<void( const wxTreeItemId& )> collect =
[&]( const wxTreeItemId& id )
{
wxCHECK_RET( id.IsOk(), wxT( "Invalid tree item" ) );
TREE_ITEM_DATA* itemData = static_cast<TREE_ITEM_DATA*>( m_tree->GetItemData( id ) );
if( id != m_tree->GetRootItem() && m_tree->ItemHasChildren( id )
&& !m_tree->IsExpanded( id ) )
{
collapsed.push_back( itemData->m_SheetPath.PathAsString() );
return;
}
wxTreeItemIdValue cookie;
wxTreeItemId child = m_tree->GetFirstChild( id, cookie );
while( child.IsOk() )
{
collect( child );
child = m_tree->GetNextChild( id, cookie );
}
};
collect( m_tree->GetRootItem() );
return collapsed;
}
void HIERARCHY_PANE::onTreeItemRightClick( wxTreeEvent& aEvent )
{
onRightClick( aEvent.GetItem() );

View File

@ -31,6 +31,8 @@
#include <wx/imaglist.h>
#include <wx/object.h> // wxRTTI macros
#include <wx/treectrl.h>
#include <set>
#include <vector>
#include "widgets/wx_panel.h"
@ -98,6 +100,11 @@ public:
*/
void UpdateLabelsHierarchyTree();
/**
* Returns a list of sheet paths for nodes that are currently collapsed.
*/
std::vector<wxString> GetCollapsedPaths() const;
private:
/**
* Create the hierarchical tree of the schematic.
@ -149,6 +156,7 @@ private:
HIERARCHY_TREE* m_tree;
bool m_events_bound;
std::set<wxString> m_collapsedPaths;
};
#endif // HIERARCHY_PANE_H

View File

@ -151,6 +151,9 @@ public:
PCB_SELECTION_FILTER_OPTIONS m_PcbSelectionFilter;
SCH_SELECTION_FILTER_OPTIONS m_SchSelectionFilter;
/// Collapsed nodes in the schematic hierarchy navigator
std::vector<wxString> m_SchHierarchyCollapsed;
// Upstream git repo info
wxString m_GitRepoUsername;
wxString m_GitRepoType;