From a74aa3850a394d5bdd18bf4d4b26a8681bb09702 Mon Sep 17 00:00:00 2001
From: Seth Hillbrand <hillbrand@ucdavis.edu>
Date: Sat, 9 Feb 2019 16:57:53 -0800
Subject: [PATCH] libtree: Update width automatically

When filtering, we update the width of the displayed column to ensure
the full text is visible to the user.  Check is rough, based on line
width (doesn't completely account for differing char widths) but is
sufficient for the approximate difference

Fixes: lp:1815401
* https://bugs.launchpad.net/kicad/+bug/1815401

Fixes: lp:1788495
* https://bugs.launchpad.net/kicad/+bug/1788495
---
 common/lib_tree_model.h           |  1 +
 common/lib_tree_model_adapter.cpp | 57 +++++++++++++++++++++++++------
 common/lib_tree_model_adapter.h   | 12 ++++---
 common/widgets/lib_tree.cpp       | 17 +++++++--
 common/widgets/lib_tree.h         |  4 +--
 5 files changed, 71 insertions(+), 20 deletions(-)

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;
 };