diff --git a/common/lib_tree_model.h b/common/lib_tree_model.h
index 970cc0dfa5..98b21fdbd7 100644
--- a/common/lib_tree_model.h
+++ b/common/lib_tree_model.h
@@ -104,6 +104,7 @@ public:
     LIB_ID      LibId;       ///< LIB_ID determined by the parent library nickname and alias name.
     int         Unit;        ///< Actual unit, or zero
     bool        IsRoot;      ///< Indicates if the symbol is a root symbol instead of an alias.
+    int         VisLen;      ///< Length of the string as shown on screen
 
     /**
      * Update the score for this part. This is accumulative - it will be
diff --git a/common/lib_tree_model_adapter.cpp b/common/lib_tree_model_adapter.cpp
index d7c15240b7..f433f1cf81 100644
--- a/common/lib_tree_model_adapter.cpp
+++ b/common/lib_tree_model_adapter.cpp
@@ -28,8 +28,6 @@
 #include <wx/wupdlock.h>
 
 
-LIB_TREE_MODEL_ADAPTER::WIDTH_CACHE LIB_TREE_MODEL_ADAPTER::m_width_cache;
-
 static const int kDataViewIndent = 20;
 
 
@@ -99,6 +97,17 @@ void LIB_TREE_MODEL_ADAPTER::ShowUnits( bool aShow )
 }
 
 
+void LIB_TREE_MODEL_ADAPTER::UpdateWidth( int aCol )
+{
+    auto col = m_widget->GetColumn( aCol );
+
+    if( col )
+    {
+        col->SetWidth( ColWidth( m_tree, aCol, col->GetTitle() ) );
+    }
+}
+
+
 void LIB_TREE_MODEL_ADAPTER::SetPreselectNode( LIB_ID const& aLibId, int aUnit )
 {
     m_preselect_lib_id = aLibId;
@@ -112,10 +121,15 @@ void LIB_TREE_MODEL_ADAPTER::DoAddLibrary( wxString const& aNodeName, wxString c
 {
     auto& lib_node = m_tree.AddLib( aNodeName, aDesc );
 
+    lib_node.VisLen = wxTheApp->GetTopWindow()->GetTextExtent( lib_node.Name ).x;
+
     for( auto item: aItemList )
     {
         if( item )
-            lib_node.AddItem( item );
+        {
+            auto& child_node = lib_node.AddItem( item );
+            child_node.VisLen = wxTheApp->GetTopWindow()->GetTextExtent( child_node.Name ).x;
+        }
     }
 
     lib_node.AssignIntrinsicRanks( presorted );
@@ -170,6 +184,8 @@ void LIB_TREE_MODEL_ADAPTER::UpdateSearchString( wxString const& aSearch )
         m_widget->Select( item );
         m_widget->EnsureVisible( item );
     }
+
+    UpdateWidth( 0 );
 }
 
 
@@ -183,10 +199,8 @@ void LIB_TREE_MODEL_ADAPTER::AttachTo( wxDataViewCtrl* aDataViewCtrl )
     wxString part_head = _( "Item" );
     wxString desc_head = _( "Description" );
 
-    m_col_part = aDataViewCtrl->AppendTextColumn( part_head, 0, wxDATAVIEW_CELL_INERT,
-                                                  ColWidth( m_tree, 0, part_head ) );
-    m_col_desc = aDataViewCtrl->AppendTextColumn( desc_head, 1, wxDATAVIEW_CELL_INERT,
-                                                  ColWidth( m_tree, 1, desc_head ) );
+    m_col_part = aDataViewCtrl->AppendTextColumn( part_head, 0, wxDATAVIEW_CELL_INERT, 360 );
+    m_col_desc = aDataViewCtrl->AppendTextColumn( desc_head, 1, wxDATAVIEW_CELL_INERT, 2000 );
 }
 
 
@@ -349,10 +363,33 @@ bool LIB_TREE_MODEL_ADAPTER::GetAttr( wxDataViewItem const&   aItem,
 
 int LIB_TREE_MODEL_ADAPTER::ColWidth( LIB_TREE_NODE& aTree, int aCol, wxString const& aHeading )
 {
-    // It's too expensive to calculate widths on really big trees, and the user probably
-    // wants it left where they dragged it anyway.
     if( aCol == 0 )
-        return 360;
+    {
+        int padding = m_widget->GetTextExtent( "MM" ).x;
+        int longest = m_widget->GetTextExtent( aHeading ).x;
+
+        for( auto& node : aTree.Children )
+        {
+            auto item = ToItem( &*node );
+
+            if( !item.IsOk() )
+                continue;
+
+            if( node->Score > 0 )
+                longest = std::max( longest, node->VisLen + padding );
+
+            if( !m_widget->IsExpanded( item ) )
+                continue;
+
+            for( auto& childNode : node->Children )
+            {
+                if( childNode->Score > 0 )
+                    longest = std::max( longest, childNode->VisLen + 2 * padding );
+            }
+        }
+
+        return longest;
+    }
     else
         return 2000;
 }
diff --git a/common/lib_tree_model_adapter.h b/common/lib_tree_model_adapter.h
index fc1b74bed8..1446cd70ce 100644
--- a/common/lib_tree_model_adapter.h
+++ b/common/lib_tree_model_adapter.h
@@ -28,6 +28,7 @@
 
 #include <wx/hashmap.h>
 #include <wx/dataview.h>
+#include <wx/headerctrl.h>
 #include <vector>
 #include <functional>
 
@@ -133,6 +134,13 @@ public:
      */
     void ShowUnits( bool aShow );
 
+    /**
+     * Update the column size based on the displayed contents
+     *
+     * @param aCol Which column to resize
+     */
+    void UpdateWidth( int aCol );
+
     /**
      * Set the component name to be selected if there are no search results.
      * May be set at any time; updates at the next UpdateSearchString().
@@ -327,10 +335,6 @@ private:
     wxDataViewColumn*   m_col_desc;
     wxDataViewCtrl*     m_widget;
 
-    WX_DECLARE_STRING_HASH_MAP( std::vector<int>, WIDTH_CACHE );
-
-    static WIDTH_CACHE m_width_cache;
-
     /**
      * Compute the width required for the given column of a node and its
      * children.
diff --git a/common/widgets/lib_tree.cpp b/common/widgets/lib_tree.cpp
index effd8bec3b..2810a26851 100644
--- a/common/widgets/lib_tree.cpp
+++ b/common/widgets/lib_tree.cpp
@@ -41,8 +41,7 @@ LIB_TREE::LIB_TREE( wxWindow* aParent, LIB_TABLE* aLibTable, LIB_TREE_MODEL_ADAP
       m_adapter( aAdapter ),
       m_query_ctrl( nullptr ),
       m_details_ctrl( nullptr ),
-      m_menuActive( false ),
-      m_filtering( false )
+      m_menuActive( false )
 {
     // create space for context menu pointers, INVALID is the max value
     m_menus.resize( LIB_TREE_NODE::TYPE::INVALID + 1 );
@@ -114,6 +113,8 @@ LIB_TREE::LIB_TREE( wxWindow* aParent, LIB_TABLE* aLibTable, LIB_TREE_MODEL_ADAP
     m_tree_ctrl->Bind( wxEVT_DATAVIEW_ITEM_ACTIVATED, &LIB_TREE::onTreeActivate, this );
     m_tree_ctrl->Bind( wxEVT_DATAVIEW_SELECTION_CHANGED, &LIB_TREE::onTreeSelect, this );
     m_tree_ctrl->Bind( wxEVT_COMMAND_DATAVIEW_ITEM_CONTEXT_MENU, &LIB_TREE::onContextMenu, this );
+    m_tree_ctrl->Bind( wxEVT_DATAVIEW_ITEM_EXPANDED, &LIB_TREE::onExpandCollapse, this );
+    m_tree_ctrl->Bind( wxEVT_DATAVIEW_ITEM_COLLAPSED, &LIB_TREE::onExpandCollapse, this );
 
     Bind( COMPONENT_PRESELECTED, &LIB_TREE::onPreselect, this );
 
@@ -129,6 +130,7 @@ LIB_TREE::LIB_TREE( wxWindow* aParent, LIB_TABLE* aLibTable, LIB_TREE_MODEL_ADAP
 
     // There may be a part preselected in the model. Make sure it is displayed.
     postPreselectEvent();
+    m_adapter->UpdateWidth( 0 );
 
     Layout();
     sizer->Fit( this );
@@ -193,7 +195,6 @@ void LIB_TREE::Regenerate( bool aKeepState )
 
     wxString filter = m_query_ctrl->GetValue();
     m_adapter->UpdateSearchString( filter );
-    m_filtering = !filter.IsEmpty();
     postPreselectEvent();
 
     // Restore the state
@@ -228,6 +229,7 @@ void LIB_TREE::selectIfValid( const wxDataViewItem& aTreeId )
     if( aTreeId.IsOk() )
     {
         m_tree_ctrl->EnsureVisible( aTreeId );
+        m_adapter->UpdateWidth( 0 );
         m_tree_ctrl->Select( aTreeId );
         postPreselectEvent();
     }
@@ -237,7 +239,10 @@ void LIB_TREE::selectIfValid( const wxDataViewItem& aTreeId )
 void LIB_TREE::centerIfValid( const wxDataViewItem& aTreeId )
 {
     if( aTreeId.IsOk() )
+    {
         m_tree_ctrl->EnsureVisible( aTreeId );
+        m_adapter->UpdateWidth( 0 );
+    }
 }
 
 
@@ -361,6 +366,12 @@ void LIB_TREE::onTreeSelect( wxDataViewEvent& aEvent )
 }
 
 
+void LIB_TREE::onExpandCollapse( wxDataViewEvent& aEvent )
+{
+    m_adapter->UpdateWidth( 0 );
+}
+
+
 void LIB_TREE::onTreeActivate( wxDataViewEvent& aEvent )
 {
     if( !GetSelectedLibId().IsValid() )
diff --git a/common/widgets/lib_tree.h b/common/widgets/lib_tree.h
index 278a099dfe..9b0e01ee9c 100644
--- a/common/widgets/lib_tree.h
+++ b/common/widgets/lib_tree.h
@@ -169,6 +169,7 @@ protected:
 
     void onTreeSelect( wxDataViewEvent& aEvent );
     void onTreeActivate( wxDataViewEvent& aEvent );
+    void onExpandCollapse( wxDataViewEvent& aEvent );
     void onUpdateUI( wxUpdateUIEvent& aEvent );
 
     void onDetailsLink( wxHtmlLinkEvent& aEvent );
@@ -188,9 +189,6 @@ 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;
-
     ///> State of the widget before any filters applied
     STATE m_unfilteredState;
 };