]> git.saurik.com Git - wxWidgets.git/commitdiff
[ 1507736 ] wxOwnerDrawnComboBox improved list item width checking.
authorWłodzimierz Skiba <abx@abx.art.pl>
Mon, 19 Jun 2006 09:02:10 +0000 (09:02 +0000)
committerWłodzimierz Skiba <abx@abx.art.pl>
Mon, 19 Jun 2006 09:02:10 +0000 (09:02 +0000)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@39790 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

include/wx/odcombo.h
src/generic/odcombo.cpp

index 0b578c6580646915ba3e08a7d1aef7af5b75e27a..39bfc73cd02ad6b19bc40b09a09ca0e2050cfe0f 100644 (file)
@@ -118,8 +118,12 @@ protected:
     // gets value, sends event and dismisses
     void DismissWithEvent();
 
-    // Re-calculates width for given item
-    void CheckWidth( int pos );
+    // OnMeasureItemWidth will be called on next GetAdjustedSize.
+    void ItemWidthChanged(unsigned int item)
+    {
+        m_widths[item] = -1;
+        m_widthsDirty = true;
+    }
 
     // Callbacks for drawing and measuring items. Override in a derived class for
     // owner-drawnness. Font, background and text colour have been prepared according
@@ -155,24 +159,35 @@ protected:
 
     wxArrayString           m_strings;
     wxArrayPtrVoid          m_clientDatas;
-    wxArrayInt              m_widths; // cached line widths
 
     wxFont                  m_useFont;
 
     //wxString                m_stringValue; // displayed text (may be different than m_strings[m_value])
     int                     m_value; // selection
     int                     m_itemHover; // on which item the cursor is
-    int                     m_widestWidth; // width of widest item thus far
-    int                     m_avgCharWidth;
-    int                     m_baseImageWidth; // how much per item drawn in addition to text
     int                     m_itemHeight; // default item height (calculate from font size
                                           // and used in the absence of callback)
     wxClientDataType        m_clientDataItemsType;
 
 private:
 
+    // Cached item widths (in pixels).
+    wxArrayInt              m_widths;
+
+    // Width of currently widest item.
+    int                     m_widestWidth;
+
+    // Index of currently widest item.
+    int                     m_widestItem;
+
+    // Measure some items in next GetAdjustedSize?
+    bool                    m_widthsDirty;
+
+    // Find widest item in next GetAdjustedSize?
+    bool                    m_findWidest;
+
     // has the mouse been released on this control?
-    bool m_clicked;
+    bool                    m_clicked;
 
     DECLARE_EVENT_TABLE()
 };
index 037fb88b43f60e787ad65e729d50e26b1b9896bb..1a1f2e74e41cb91a53ffa8cfe6efc977f6b10f54 100644 (file)
@@ -58,8 +58,9 @@ END_EVENT_TABLE()
 void wxVListBoxComboPopup::Init()
 {
     m_widestWidth = 0;
-    m_avgCharWidth = 0;
-    m_baseImageWidth = 0;
+    m_widestItem = -1;
+    m_widthsDirty = false;
+    m_findWidest = false;
     m_itemHeight = 0;
     m_value = -1;
     m_itemHover = -1;
@@ -327,26 +328,6 @@ void wxVListBoxComboPopup::OnKey(wxKeyEvent& event)
         event.Skip();
 }
 
-void wxVListBoxComboPopup::CheckWidth( int pos )
-{
-    wxCoord x = OnMeasureItemWidth(pos);
-
-    if ( x < 0 )
-    {
-        if ( !m_useFont.Ok() )
-            m_useFont = m_combo->GetFont();
-
-        wxCoord y;
-        m_combo->GetTextExtent(m_strings[pos], &x, &y, 0, 0, &m_useFont);
-        x += 4;
-    }
-
-    if ( m_widestWidth < x )
-    {
-        m_widestWidth = x;
-    }
-}
-
 void wxVListBoxComboPopup::Insert( const wxString& item, int pos )
 {
     // Need to change selection?
@@ -358,12 +339,11 @@ void wxVListBoxComboPopup::Insert( const wxString& item, int pos )
     }
 
     m_strings.Insert(item,pos);
+    m_widths.Insert(-1,pos);
+    m_widthsDirty = true;
 
     if ( IsCreated() )
         wxVListBox::SetItemCount( wxVListBox::GetItemCount()+1 );
-
-    // Calculate width
-    CheckWidth(pos);
 }
 
 int wxVListBoxComboPopup::Append(const wxString& item)
@@ -397,6 +377,10 @@ void wxVListBoxComboPopup::Clear()
     wxASSERT(m_combo);
 
     m_strings.Empty();
+    m_widths.Empty();
+
+    m_widestWidth = 0;
+    m_widestItem = -1;
 
     ClearClientDatas();
 
@@ -427,6 +411,8 @@ void wxVListBoxComboPopup::SetItemClientData( unsigned int n,
 
     m_clientDatas.SetCount(n+1,NULL);
     m_clientDatas[n] = clientData;
+
+    ItemWidthChanged(n);
 }
 
 void* wxVListBoxComboPopup::GetItemClientData(unsigned int n) const
@@ -449,6 +435,10 @@ void wxVListBoxComboPopup::Delete( unsigned int item )
     }
 
     m_strings.RemoveAt(item);
+    m_widths.RemoveAt(item);
+
+    if ( (int)item == m_widestItem )
+        m_findWidest = true;
 
     if ( IsCreated() )
         wxVListBox::SetItemCount( wxVListBox::GetItemCount()-1 );
@@ -472,6 +462,7 @@ wxString wxVListBoxComboPopup::GetString( int item ) const
 void wxVListBoxComboPopup::SetString( int item, const wxString& str )
 {
     m_strings[item] = str;
+    ItemWidthChanged(item);
 }
 
 wxString wxVListBoxComboPopup::GetStringValue() const
@@ -537,6 +528,92 @@ wxSize wxVListBoxComboPopup::GetAdjustedSize( int minWidth, int prefHeight, int
     else
         height = 50;
 
+    bool doFindWidest = m_findWidest;
+
+    // Measure items with dirty width.
+    if ( m_widthsDirty )
+    {
+        unsigned int i;
+        unsigned int n = m_widths.GetCount();
+        int dirtyHandled = 0;
+        wxArrayInt& widths = m_widths;
+
+        // I think using wxDC::GetTextExtent is faster than
+        // wxWindow::GetTextExtent (assuming same dc is used
+        // for all calls, as we do here).
+        wxClientDC dc(m_combo);
+        dc.SetFont(m_useFont);
+
+        for ( i=0; i<n; i++ )
+        {
+            if ( widths[i] < 0 )
+            {
+                wxCoord x = OnMeasureItemWidth(i);
+
+                if ( x < 0 )
+                {
+                    const wxString& text = m_strings[i];
+
+                    // To make sure performance won't suck in extreme scenarios,
+                    // we'll estimate length after some arbitrary number of items
+                    // have been checked precily.
+                    if ( dirtyHandled < 1024 )
+                    {
+                        wxCoord y;
+                        dc.GetTextExtent(text, &x, &y, 0, 0);
+                        x += 4;
+                    }
+                    else
+                    {
+                        x = text.length() * (dc.GetCharWidth()+1);
+                    }
+                }
+
+                widths[i] = x;
+
+                if ( x >= m_widestWidth )
+                {
+                    m_widestWidth = x;
+                    m_widestItem = (int)i;
+                }
+                else if ( (int)i == m_widestItem )
+                {
+                    // Width of previously widest item has been decreased, so
+                    // we'll have to check all to find current widest item.
+                    doFindWidest = true;
+                }
+
+                dirtyHandled++;
+            }
+        }
+
+        m_widthsDirty = false;
+    }
+
+    if ( doFindWidest )
+    {
+        unsigned int i;
+        unsigned int n = m_widths.GetCount();
+
+        int bestWidth = -1;
+        int bestIndex = -1;
+
+        for ( i=0; i<n; i++ )
+        {
+            int w = m_widths[i];
+            if ( w > bestWidth )
+            {
+                bestIndex = (int)i;
+                bestWidth = w;
+            }
+        }
+
+        m_widestWidth = bestWidth;
+        m_widestItem = bestIndex;
+
+        m_findWidest = false;
+    }
+
     // Take scrollbar into account in width calculations
     int widestWidth = m_widestWidth + wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
     return wxSize(minWidth > widestWidth ? minWidth : widestWidth,
@@ -554,9 +631,11 @@ void wxVListBoxComboPopup::Populate( const wxArrayString& choices )
     {
         const wxString& item = choices.Item(i);
         m_strings.Add(item);
-        CheckWidth(i);
     }
 
+    m_widths.SetCount(n,-1);
+    m_widthsDirty = true;
+
     if ( IsCreated() )
         wxVListBox::SetItemCount(n);