]> git.saurik.com Git - wxWidgets.git/commitdiff
Implement wxCOL_WIDTH_AUTOSIZE on OS X.
authorVáclav Slavík <vslavik@fastmail.fm>
Fri, 7 Jan 2011 18:26:58 +0000 (18:26 +0000)
committerVáclav Slavík <vslavik@fastmail.fm>
Fri, 7 Jan 2011 18:26:58 +0000 (18:26 +0000)
Only Cocoa build on 10.5+ is supported. Before that, NSOutlineView
didn't have reasonable support for determining cell sizes.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@66634 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

include/wx/osx/carbon/dataview.h
include/wx/osx/cocoa/dataview.h
include/wx/osx/core/dataview.h
include/wx/osx/dataview.h
interface/wx/headercol.h
src/osx/carbon/dataview.cpp
src/osx/cocoa/dataview.mm
src/osx/dataview_osx.cpp

index f4d8f93fd67459c026b211f29ec781e8ced40ed0..056a2a895c29ce06b4dee2750ae8b9c40640e036 100644 (file)
@@ -376,6 +376,7 @@ public:
   virtual wxDataViewColumn* GetColumn          (unsigned int pos) const;
   virtual int               GetColumnPosition  (wxDataViewColumn const* columnPtr) const;
   virtual bool              InsertColumn       (unsigned int pos, wxDataViewColumn* columnPtr);
+  virtual void              FitColumnWidthToContent(unsigned int WXUNUSED(pos)) { /*not implemented*/ }
 
  //
  // item related methods (inherited from wxDataViewWidgetImpl)
index f34720bb77b6c339d35d8faed9edf6e0cf9717f2..335bce5ad718c309272492d776b521bb07f64cc2 100644 (file)
@@ -441,6 +441,7 @@ public:
     virtual wxDataViewColumn* GetColumn(unsigned int pos) const;
     virtual int GetColumnPosition(wxDataViewColumn const* columnPtr) const;
     virtual bool InsertColumn(unsigned int pos, wxDataViewColumn* columnPtr);
+    virtual void FitColumnWidthToContent(unsigned int pos);
 
     // item related methods (inherited from wxDataViewWidgetImpl)
     virtual bool Add(const wxDataViewItem& parent, const wxDataViewItem& item);
index a35f7324d50155313e2fc78028b20be16f42bcd1..a478d83d12fbc80ad9e5a40b7c4e559d402e5db0 100644 (file)
@@ -58,6 +58,7 @@ public:
   virtual int               GetColumnPosition  (wxDataViewColumn const* columnPtr) const       = 0; // returns the position of the passed column in the native control
   virtual bool              InsertColumn       (unsigned int pos, wxDataViewColumn* columnPtr) = 0; // inserts a column at pos in the native control;
                                                                                                     // the method can assume that the column's owner is already set
+  virtual void              FitColumnWidthToContent(unsigned int pos)                          = 0; // resizes column to fit its content
 
  //
  // item related methods
index 34e114a5a2ef256182154f66e1ec3a8e8471bfee..ae6866359976a5c6d30c720273c4fda5a4569fd6 100644 (file)
@@ -95,8 +95,8 @@ private:
         m_flags = flags & ~wxDATAVIEW_COL_HIDDEN; // TODO
         m_maxWidth = 30000;
         m_minWidth = 0;
-        m_width = width >= 0 ? width : wxDVC_DEFAULT_WIDTH;
         m_alignment = align;
+        SetWidth(width);
     }
 
     bool m_ascending; // sorting order
index e0a65b9e62815faf7d109eb1813fdd9ac6af6e5d..b8509e8423abe32a1de3a7d14f071d6c396619c0 100644 (file)
@@ -16,7 +16,12 @@ enum
     /// Special value used for column width meaning unspecified or default.
     wxCOL_WIDTH_DEFAULT = -1,
 
-    /// Size the column automatically to fit all values.
+    /**
+        Size the column automatically to fit all values.
+
+        @note On OS X, this style is only implemented in the Cocoa build on
+              OS X >= 10.5; it behaves identically to wxCOL_WIDTH_DEFAULT otherwise.
+     */
     wxCOL_WIDTH_AUTOSIZE = -2
 };
 
index 48cb63956e9823a54e5df72e0fd3db75dc780ab3..680d368ea0962d073e3ccca064c020790ce902dd 100644 (file)
@@ -2746,6 +2746,16 @@ void wxDataViewColumn::SetWidth(int width)
 {
   wxDataViewCtrl* dataViewCtrlPtr(GetOwner());
 
+  switch ( width )
+  {
+    case wxCOL_WIDTH_AUTOSIZE:
+       // not implemented, fall through
+    case wxCOL_WIDTH_DEFAULT:
+      width = wxDVC_DEFAULT_WIDTH;
+      break;
+    default:
+      break;
+  }
 
   if ((width >= m_minWidth) && (width <= m_maxWidth))
   {
index 80505a0f3c704ea7376c2ffda9aea2199a606634..c2132ae5343e9036cc7157f8d359e27716d52be3 100644 (file)
@@ -24,6 +24,7 @@
 #include "wx/osx/private.h"
 #include "wx/osx/cocoa/dataview.h"
 #include "wx/renderer.h"
+#include "wx/stopwatch.h"
 
 // ============================================================================
 // Constants used locally
@@ -321,7 +322,6 @@ NSTableColumn* CreateNativeColumn(const wxDataViewColumn *column)
     );
 
     // setting the size related parameters:
-    const int width = column->GetWidthVariable();
     int resizingMask;
     if (column->IsResizeable())
     {
@@ -340,7 +340,6 @@ NSTableColumn* CreateNativeColumn(const wxDataViewColumn *column)
                             : NSTableColumnNoResizing;
     }
     [nativeColumn setResizingMask:resizingMask];
-    [nativeColumn setWidth:width];
 
 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
     // setting the visibility:
@@ -1945,10 +1944,132 @@ bool wxCocoaDataViewControl::InsertColumn(unsigned int pos, wxDataViewColumn* co
     [m_OutlineView addTableColumn:nativeColumn];
     if (pos != static_cast<unsigned int>([m_OutlineView numberOfColumns]-1))
         [m_OutlineView moveColumn:[m_OutlineView numberOfColumns]-1 toColumn:pos];
+
+    // set columns width now that it can be computed even for autosized columns:
+    columnPtr->SetWidth(columnPtr->GetWidthVariable());
+
     // done:
     return true;
 }
 
+void wxCocoaDataViewControl::FitColumnWidthToContent(unsigned int pos)
+{
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
+    const int count = GetCount();
+    NSTableColumn *column = GetColumn(pos)->GetNativeData()->GetNativeColumnPtr();
+
+    class MaxWidthCalculator
+    {
+    public:
+        MaxWidthCalculator(wxCocoaOutlineView *view,
+                           NSTableColumn *column, unsigned columnIndex)
+            : m_width(0),
+              m_view(view),
+              m_column(columnIndex),
+              m_indent(0)
+        {
+            // account for indentation in the column with expander
+            if ( column == [m_view outlineTableColumn] )
+                m_indent = [m_view indentationPerLevel];
+        }
+
+        void UpdateWithWidth(int width)
+        {
+            m_width = wxMax(m_width, width);
+        }
+
+        void UpdateWithRow(int row)
+        {
+            NSCell *cell = [m_view preparedCellAtColumn:m_column row:row];
+            unsigned cellWidth = [cell cellSize].width + 1/*round the float up*/;
+
+            if ( m_indent )
+                cellWidth += m_indent * ([m_view levelForRow:row] + 1);
+
+            m_width = wxMax(m_width, cellWidth);
+        }
+
+        int GetMaxWidth() const { return m_width; }
+
+    private:
+        int m_width;
+        wxCocoaOutlineView *m_view;
+        unsigned m_column;
+        int m_indent;
+    };
+
+    MaxWidthCalculator calculator(m_OutlineView, column, pos);
+
+    if ( [column headerCell] )
+    {
+        calculator.UpdateWithWidth([[column headerCell] cellSize].width + 1/*round the float up*/);
+    }
+
+    // The code below deserves some explanation. For very large controls, we
+    // simply can't afford to calculate sizes for all items, it takes too
+    // long. So the best we can do is to check the first and the last N/2
+    // items in the control for some sufficiently large N and calculate best
+    // sizes from that. That can result in the calculated best width being too
+    // small for some outliers, but it's better to get slightly imperfect
+    // result than to wait several seconds after every update. To avoid highly
+    // visible miscalculations, we also include all currently visible items
+    // no matter what.  Finally, the value of N is determined dynamically by
+    // measuring how much time we spent on the determining item widths so far.
+
+#if wxUSE_STOPWATCH
+    int top_part_end = count;
+    static const long CALC_TIMEOUT = 20/*ms*/;
+    // don't call wxStopWatch::Time() too often
+    static const unsigned CALC_CHECK_FREQ = 100;
+    wxStopWatch timer;
+#else
+    // use some hard-coded limit, that's the best we can do without timer
+    int top_part_end = wxMin(500, count);
+#endif // wxUSE_STOPWATCH/!wxUSE_STOPWATCH
+
+    int row = 0;
+
+    for ( row = 0; row < top_part_end; row++ )
+    {
+#if wxUSE_STOPWATCH
+        if ( row % CALC_CHECK_FREQ == CALC_CHECK_FREQ-1 &&
+             timer.Time() > CALC_TIMEOUT )
+            break;
+#endif // wxUSE_STOPWATCH
+        calculator.UpdateWithRow(row);
+    }
+
+    // row is the first unmeasured item now; that's our value of N/2
+
+    if ( row < count )
+    {
+        top_part_end = row;
+
+        // add bottom N/2 items now:
+        const int bottom_part_start = wxMax(row, count - row);
+        for ( row = bottom_part_start; row < count; row++ )
+            calculator.UpdateWithRow(row);
+
+        // finally, include currently visible items in the calculation:
+        const NSRange visible = [m_OutlineView rowsInRect:[m_OutlineView visibleRect]];
+        const int first_visible = wxMax(visible.location, top_part_end);
+        const int last_visible = wxMin(first_visible + visible.length, bottom_part_start);
+
+        for ( row = first_visible; row < last_visible; row++ )
+            calculator.UpdateWithRow(row);
+
+        wxLogTrace("dataview",
+                   "determined best size from %d top, %d bottom plus %d more visible items out of %d total",
+                   top_part_end,
+                   count - bottom_part_start,
+                   wxMax(0, last_visible - first_visible),
+                   count);
+    }
+
+    [column setWidth:calculator.GetMaxWidth()];
+#endif // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
+}
+
 //
 // item related methods (inherited from wxDataViewWidgetImpl)
 //
@@ -3025,8 +3146,29 @@ void wxDataViewColumn::SetTitle(const wxString& title)
 
 void wxDataViewColumn::SetWidth(int width)
 {
-    [m_NativeDataPtr->GetNativeColumnPtr() setWidth:width];
     m_width = width;
+
+    switch ( width )
+    {
+        case wxCOL_WIDTH_AUTOSIZE:
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
+            if ( GetOwner() )
+            {
+                wxCocoaDataViewControl *peer = static_cast<wxCocoaDataViewControl*>(GetOwner()->GetPeer());
+                peer->FitColumnWidthToContent(GetOwner()->GetColumnPosition(this));
+                break;
+            }
+#endif
+            // fall through if unsupported (OSX < 10.5) or not yet settable
+
+        case wxCOL_WIDTH_DEFAULT:
+            width = wxDVC_DEFAULT_WIDTH;
+            // fall through
+
+        default:
+            [m_NativeDataPtr->GetNativeColumnPtr() setWidth:width];
+            break;
+    }
 }
 
 void wxDataViewColumn::SetAsSortKey(bool WXUNUSED(sort))
index 1dabd9f4bc194b9bb6c3a7ab0f58066939ad2412..199cd2e74a8540e6431b094fc9f7f92d4bc7cafe 100644 (file)
@@ -76,6 +76,8 @@ protected:
   void AdjustRowHeight(wxDataViewItem const& item);
  // ... and the same method for a couple of items:
   void AdjustRowHeights(wxDataViewItemArray const& items);
+ // adjust wxCOL_WIDTH_AUTOSIZE columns to fit the data
+  void AdjustAutosizedColumns();
 
 private:
   wxDataViewCtrl* m_DataViewCtrlPtr;
@@ -130,6 +132,7 @@ bool wxOSXDataViewModelNotifier::ItemChanged(wxDataViewItem const& item)
     m_DataViewCtrlPtr->HandleWindowEvent(dataViewEvent);
    // row height may have to be adjusted:
     AdjustRowHeight(item);
+    AdjustAutosizedColumns();
    // done
     return true;
   }
@@ -156,6 +159,7 @@ bool wxOSXDataViewModelNotifier::ItemsChanged(wxDataViewItemArray const& items)
       return false;
  // if this location is reached all items have been updated:
   AdjustRowHeights(items);
+  AdjustAutosizedColumns();
  // done:
   return true;
 }
@@ -174,6 +178,8 @@ bool wxOSXDataViewModelNotifier::ItemDeleted(wxDataViewItem const& parent, wxDat
   noFailureFlag = m_DataViewCtrlPtr->GetDataViewPeer()->Remove(parent,item);
  // enable automatic updating again:
   m_DataViewCtrlPtr->SetDeleting(false);
+
+  AdjustAutosizedColumns();
  // done:
   return noFailureFlag;
 }
@@ -192,6 +198,8 @@ bool wxOSXDataViewModelNotifier::ItemsDeleted(wxDataViewItem const& parent, wxDa
   noFailureFlag = m_DataViewCtrlPtr->GetDataViewPeer()->Remove(parent,items);
  // enable automatic updating again:
   m_DataViewCtrlPtr->SetDeleting(false);
+
+  AdjustAutosizedColumns();
  // done:
   return noFailureFlag;
 }
@@ -209,6 +217,8 @@ bool wxOSXDataViewModelNotifier::ValueChanged(wxDataViewItem const& item, unsign
     dataViewEvent.SetItem(item);
    // send the equivalent wxWidget event:
     m_DataViewCtrlPtr->HandleWindowEvent(dataViewEvent);
+
+    AdjustAutosizedColumns();
    // done
     return true;
   }
@@ -292,6 +302,18 @@ void wxOSXDataViewModelNotifier::AdjustRowHeights(wxDataViewItemArray const& ite
   }
 }
 
+void wxOSXDataViewModelNotifier::AdjustAutosizedColumns()
+{
+  unsigned count = m_DataViewCtrlPtr->GetColumnCount();
+  for ( unsigned col = 0; col < count; col++ )
+  {
+      wxDataViewColumn *column = m_DataViewCtrlPtr->GetColumnPtr(col);
+
+      if ( column->GetWidthVariable() == wxCOL_WIDTH_AUTOSIZE )
+        m_DataViewCtrlPtr->GetDataViewPeer()->FitColumnWidthToContent(col);
+  }
+}
+
 // ---------------------------------------------------------
 // wxDataViewCustomRenderer
 // The constructor, the implementation macro and environment