From 0af5bb05fca54452c76571a07337ffa54458e4e9 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sat, 3 Jan 2009 15:44:29 -0800 Subject: [PATCH 1/2] Replacing a few instances of Math.min/max with MIN/MAX and == with ===. Reviewed by me. --- Foundation/CPRange.j | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Foundation/CPRange.j b/Foundation/CPRange.j index ebdee50c9..2d6519784 100755 --- a/Foundation/CPRange.j +++ b/Foundation/CPRange.j @@ -62,7 +62,7 @@ function CPMakeRangeCopy(aRange) */ function CPEmptyRange(aRange) { - return aRange.length == 0; + return aRange.length === 0; } /*! @@ -84,7 +84,7 @@ function CPMaxRange(aRange) */ function CPEqualRanges(lhsRange, rhsRange) { - return ((lhsRange.location == rhsRange.location) && (lhsRange.length == rhsRange.length)); + return ((lhsRange.location === rhsRange.location) && (lhsRange.length === rhsRange.length)); } /*! @@ -109,8 +109,8 @@ function CPLocationInRange(aLocation, aRange) */ function CPUnionRange(lhsRange, rhsRange) { - var location = Math.min(lhsRange.location, rhsRange.location); - return CPMakeRange(location, Math.max(CPMaxRange(lhsRange), CPMaxRange(rhsRange)) - location); + var location = MIN(lhsRange.location, rhsRange.location); + return CPMakeRange(location, MAX(CPMaxRange(lhsRange), CPMaxRange(rhsRange)) - location); } /*! @@ -125,8 +125,8 @@ function CPIntersectionRange(lhsRange, rhsRange) if(CPMaxRange(lhsRange) < rhsRange.location || CPMaxRange(rhsRange) < lhsRange.location) return CPMakeRange(0, 0); - var location = Math.max(lhsRange.location, rhsRange.location); - return CPMakeRange(location, Math.min(CPMaxRange(lhsRange), CPMaxRange(rhsRange)) - location); + var location = MAX(lhsRange.location, rhsRange.location); + return CPMakeRange(location, MIN(CPMaxRange(lhsRange), CPMaxRange(rhsRange)) - location); } /*! From ac8ecdb7f3ab9c1549a6d724f5c414250244be43 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sat, 3 Jan 2009 16:16:24 -0800 Subject: [PATCH 2/2] A number of CPTableView improvements, still not built by default. Reviewed by me. --- AppKit/CPTableColumn.j | 133 +++++++++++++- AppKit/CPTableView.j | 393 +++++++++++++++++++++++++++++++---------- 2 files changed, 422 insertions(+), 104 deletions(-) diff --git a/AppKit/CPTableColumn.j b/AppKit/CPTableColumn.j index 2407b4c3a..1421eecb1 100644 --- a/AppKit/CPTableColumn.j +++ b/AppKit/CPTableColumn.j @@ -43,6 +43,10 @@ CPTableColumnAutoresizingMask = 1; */ CPTableColumnUserResizingMask = 2; +#define PurgableInfoMake(aView, aRow) { view:(aView), row:(aRow) } +#define PurgableInfoView(anInfo) ((anInfo).view) +#define PurgableInfoRow(anInfo) ((anInfo).row) + /*! @class CPTableColumn An CPTableColumn object mainly keeps information about the width of the column, its minimum and maximum width; whether the column can be edited or resized; and the cells used to draw the column header and the data in the column. You can change all these attributes of the column by calling the appropriate methods. Please note that the table column does not hold nor has access to the data to be displayed in the column; this data is maintained in the table view's data source.

@@ -54,6 +58,7 @@ CPTableColumnUserResizingMask = 2; @implementation CPTableColumn : CPObject { CPString _identifier; + CPView _headerView; CPTableView _tableView; @@ -62,6 +67,12 @@ CPTableColumnUserResizingMask = 2; float _maxWidth; unsigned _resizingMask; + + CPView _dataView; + + Object _dataViewData; + Object _dataViewForView; + Object _purgableInfosForDataView; } /*! @@ -81,8 +92,14 @@ CPTableColumnUserResizingMask = 2; _minWidth = 8.0; _maxWidth = 1000.0; - // FIXME - _dataCell = [[CPTextField alloc] initWithFrame:CPRectMakeZero()]; + _dataViewData = {}; + _dataViewForView = {}; + _purgableInfosForDataView = {}; + + [self setDataView:[[CPTextField alloc] initWithFrame:CPRectMakeZero()]]; + + _headerView = [[CPTextField alloc] initWithFrame:CPRectMakeZero()]; + [_headerView setBackgroundColor:[CPColor greenColor]]; } return self; @@ -229,13 +246,16 @@ CPTableColumnUserResizingMask; return _isEditable; } +//Setting the column header view + /*! Sets the view that draws the column's header. @param aHeaderView the view that will draws the column header */ -- (void)setHeaderView:(CPView)aHeaderView + +- (void)setHeaderView:(CPView)aView { - _headerView = aHeaderView; + _headerView = aView; } /*! @@ -249,9 +269,21 @@ CPTableColumnUserResizingMask; /*! Sets the data cell that draws rows in this column. */ -- (void)setDataCell:(CPCell)aDataCell +- (void)setDataCell:(CPView )aView { - _dataCell = aDataCell; + [self setDataView:aView]; +} + +/* + Sets the data view that draws rows in this column. +*/ +- (void)setDataView:(CPView )aView +{ + if (_dataView) + _dataViewData[[_dataView hash]] = nil; + + _dataView = aView; + _dataViewData[[aView hash]] = [CPKeyedArchiver archivedDataWithRootObject:aView]; } /*! @@ -259,7 +291,15 @@ CPTableColumnUserResizingMask; */ - (CPCell)dataCell { - return _dataCell; + return _dataView; +} + +/* + Returns the data view that draws rows in this column +*/ +- (CPView)dataView +{ + return [self dataCell]; } /*! @@ -270,7 +310,84 @@ CPTableColumnUserResizingMask; */ - (CPCell)dataCellForRow:(int)aRowIndex { - return [self dataCell]; + return [self dataView]; +} + +- (CPView)dataViewForRow:(int)aRowIndex +{ + return [self dataCellForRow:aRowIndex]; +} + +- (void)_markViewAsPurgable:(CPView)aView +{ + var viewHash = [aView hash], + dataViewHash = [_dataViewForView[viewHash] hash]; + + if (!_purgableInfosForDataView[dataViewHash]) + _purgableInfosForDataView[dataViewHash] = [CPDictionary dictionary]; + + [_purgableInfosForDataView[dataViewHash] setObject:aView forKey:viewHash]; +} + +- (void)_markView:(CPView)aView inRow:(unsigned)aRow asPurgable:(BOOL)isPurgable +{ + var viewHash = [aView hash], + dataViewHash = [_dataViewForView[viewHash] hash]; + + if (!_purgableInfosForDataView[dataViewHash]) + { + if (!isPurgable) + return; + + _purgableInfosForDataView[dataViewHash] = [CPDictionary dictionary]; + } + + if (!isPurgable) + [_purgableInfosForDataView[dataViewHash] removeObjectForKey:viewHash]; + else + [_purgableInfosForDataView[dataViewHash] setObject:PurgableInfoMake(aView, aRow) forKey:viewHash]; +} + +- (CPView)_newDataViewForRow:(int)aRowIndex avoidingRows:(CPRange)rows +{ + var view = [self dataViewForRow:aRowIndex], + viewHash = [view hash], + dataViewHash = [_dataViewForView[viewHash] hash], + purgableInfos = _purgableInfosForDataView[dataViewHash]; + //console.warn("ok, a cell is needed"); + if (purgableInfos && [purgableInfos count]) + {//console.warn("yes, inside"); + var keys = [purgableInfos allKeys], + count = keys.length; + + while (count--) + { + var key = keys[count], + info = [purgableInfos objectForKey:key]; + + [purgableInfos removeObjectForKey:key]; + + if (CPLocationInRange(PurgableInfoRow(info), rows)) + continue; + //console.warn("yes, a purged view is usable, its called" + PurgableInfoView(info)); + return PurgableInfoView(info); + } + } + + var data = _dataViewData[viewHash]; + + if (!data) + { + _dataViewData[viewHash] = [CPKeyedArchiver archivedDataWithRootObject:view]; + data = _dataViewData[viewHash]; + } + //console.warn("nope, time for creation"); + return [CPKeyedUnarchiver unarchiveObjectWithData:data]; } @end + +_PurgableViewInfoMake = function(aView, aRow) +{ + return { view:aView, row:aRow}; +} diff --git a/AppKit/CPTableView.j b/AppKit/CPTableView.j index cf4bcd744..87362ebd5 100644 --- a/AppKit/CPTableView.j +++ b/AppKit/CPTableView.j @@ -20,11 +20,12 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -import "CPControl.j" -import "CPTableColumn.j" +@import "CPControl.j" +@import "CPTableColumn.j" +//objj_backtrace_set_enable(true); -var CPTableViewCellPlaceholder = nil; +#define ROW_HEIGHT(aRow) (_hasVariableHeightRows ? _rowHeights[aRow] : _rowHeight); /* CPTableView is located within the AppKit framework and is used to display tables. It uses a delegate model for getting its data i.e. you give it an object that provides it with the data it should display. @@ -33,28 +34,40 @@ var CPTableViewCellPlaceholder = nil; */ @implementation CPTableView : CPControl { - id _dataSource; + id _dataSource; CPScrollView _scrollView; CPTableHeaderView _headerView; - CPArray _tableColumns; + CPArray _tableColumns; + // unsigned _numberOfRows; unsigned _numberOfColumns; + // Heights + float _rowHeight; + float _columnHeight; // calculated + + CPArray _rowMinYs; + CPArray _rowHeights; + + BOOL _hasVariableHeightRows; + CPArray _tableCells; CPArray _tableColumnViews; - CGSize _intercellSpacing; -} - -+ (void)initialize -{ - if (self != [CPTableView class]) - return; + CGSize _intercellSpacing; - CPTableViewCellPlaceholder = [[CPObject alloc] init]; + // Caching + Object _dataViewCache; + CPArray _objectValueCache; + + CPRange _visibleRows; + CPRange _visibleColumns; + + CPRange _populatedRows; + CPRange _populatedColumns; } - (id)initWithFrame:(CGRect)aFrame @@ -62,27 +75,20 @@ var CPTableViewCellPlaceholder = nil; self = [super initWithFrame:aFrame]; if (self) - { - /*_scrollView = [[CPView alloc] initWithFrame:CPRectMakeZero()]; - _headerView = [[CPView alloc] initWithFrame:CPRectMakeZero()]; - - [_scrollView setBackgroundColor:[CPColor redColor]]; - [_headerView setBackgroundColor:[CPColor blueColor]]; - - [self addSubview:_scrollView]; - [self addSubview:_headerView]; - - [self tile];*/ - - //[self setBackgroundColor:[CPColor redColor]]; - + { _rowHeight = 17.0; - _tableCells = [[CPArray alloc] init]; - _tableColumns = [[CPArray alloc] init]; - _tableColumnViews = [[CPArray alloc] init]; + _tableCells = []; + _tableColumns = []; + _tableColumnViews = []; + _dataViewCache = {}; + _objectValueCache = {}; + _intercellSpacing = CPSizeMake(3.0, 2.0); + + _visibleRows = CPMakeRange(0, 0); + _visibleColumns = CPMakeRange(0, 0); } return self; @@ -91,52 +97,186 @@ var CPTableViewCellPlaceholder = nil; /* Returns the table's column height */ -- (float)columnHeight +- (float)_columnHeight { - var bounds = [self bounds], - height = _numberOfRows * (_rowHeight + _intercellSpacing.height); + return _numberOfRows * (_rowHeight + _intercellSpacing.height); +} + +- (void)newCellForRow:(unsigned)aRowIndex column:(unsigned)aColumnIndex avoidingRows:(CPRange)rows +{//console.warn("new cell please."); + var dataView = [_tableColumns[aColumnIndex] _newDataViewForRow:aRowIndex avoidingRows:rows]; + + [dataView setFrame:CGRectMake(0.0, aRowIndex * (_rowHeight + _intercellSpacing.height), [_tableColumns[aColumnIndex] width], _rowHeight)]; + [dataView setBackgroundColor:[CPColor greenColor]]; - return CPRectGetHeight(bounds) > height ? CPRectGetHeight(bounds) : height; + if (!_objectValueCache[aColumnIndex]) + _objectValueCache[aColumnIndex] = []; + + // We may be storing 0 after all! + if (typeof _objectValueCache[aColumnIndex][aRowIndex] === "undefined") + _objectValueCache[aColumnIndex][aRowIndex] = [_dataSource tableView:self objectValueForTableColumn:_tableColumns[aColumnIndex] row:aRowIndex]; + + [dataView setObjectValue:_objectValueCache[aColumnIndex][aRowIndex]]; + + return dataView; } - (void)loadTableCellsInRect:(CGRect)aRect { if (!_dataSource) return; + + // Determine new visible rows and columns. + + // Use a ambitious estimate for our starting row. + var rowStart = MAX(FLOOR((CGRectGetMinY(aRect) + _intercellSpacing.height) / (_rowHeight + _intercellSpacing.height)), 0), - // Use a ambitious estimate for our starting row. - var rows = CPMakeRange(MAX(Math.floor((CPRectGetMinY(aRect) + _intercellSpacing.height) / (_rowHeight + _intercellSpacing.height)), 0), 1); + // Use a conservative estimate for the final row. + rowEnd = MIN(_numberOfRows, CEIL(CGRectGetMaxY(aRect) / (_rowHeight + _intercellSpacing.height))), + + visibleRows = CPMakeRange(rowStart, rowEnd - rowStart); + + var columnStart = 0; + + // Iterate through all our columns until we find the first one that intersects the rect. + while (columnStart < _numberOfColumns && !CGRectIntersectsRect([_tableColumnViews[columnStart] frame], aRect)) + ++columnStart; + + // Now use a binary search to find the last visible column + // O (lg n) < O (n), but O(n) (above), so O (n + lg n) = O (n) ? + var first = columnStart + 1, + last = _numberOfColumns - 1; + columnEnd = columnStart; - // Use a conservative estimate for the final row. - rows.length = MIN(_numberOfRows, Math.ceil(CPRectGetMaxY(aRect) / (_rowHeight + _intercellSpacing.height))) - rows.location; - - var columns = CPMakeRange(0, 1); - - // Iterate through all our columns until we find one that intersects the rect. - while (columns.location < _numberOfColumns && !CPRectIntersectsRect([_tableColumnViews[columns.location] frame], aRect)) - ++columns.location; - - // Now iterate through our columns until we find one that doesn't intersect our rect. - while (CPMaxRange(columns) < _numberOfColumns && CPRectIntersectsRect([_tableColumnViews[CPMaxRange(columns)] frame], aRect)) - ++columns.length; - - var row = rows.location, - column = 0; - - for (; row < CPMaxRange(rows); ++row) - for (column = columns.location; column < CPMaxRange(columns); ++column) - { - if (!_tableCells[column][row] || _tableCells[column][row] == CPTableViewCellPlaceholder) - { - _tableCells[column][row] = [[_tableColumns[column] dataCellForRow:row] copy]; - - [_tableCells[column][row] setFrame:CPRectMake(0.0, row * (_rowHeight + _intercellSpacing.height), [_tableColumns[column] width], _rowHeight)]; - //[_tableCells[column][row] setBackgroundColor:[CPColor blueColor]]; - [_tableColumnViews[column] addSubview:_tableCells[column][row]]; - } + while (first <= last) + { + // Assume this is the one. + var columnEnd = FLOOR((first + last) / 2), + columnIsVisible = CGRectIntersectsRect([_tableColumnViews[columnEnd] frame], aRect); - [_tableCells[column][row] setObjectValue:[_dataSource tableView:self objectValueForTableColumn:_tableColumns[column] row:row]]; + // If the column isn't visible, look left! + if (!columnIsVisible) + last = columnEnd - 1; + + // Visible, nothing to the right, found it... + if (columnEnd + 1 >= _numberOfColumns) + break; + + // Visible, column to the right is NOT visible, found it! (the good way) + if (!CGRectIntersectsRect([_tableColumnViews[columnEnd + 1] frame], aRect)) + break; + + // If not, look right! (2 since we checked the dude to the right already) + first = columnEnd + 2; + } + + // columnEnd is our "count" in loops. + ++columnEnd; + + var visibleColumns = CPMakeRange(columnStart, columnEnd - columnStart); + + if (CPEqualRanges(_visibleRows, visibleRows) && CPEqualRanges(_visibleColumns, visibleColumns)) + return; + + var unionVisibleRows = CPUnionRange(_visibleRows, visibleRows), + unionVisibleColumns = CPUnionRange(_visibleColumns, visibleColumns); + + // Determine whether to use 2 sweeps or one. If we have lots of overlap of cells, use just one. + if (unionVisibleRows.length * unionVisibleColumns.length <= + (_visibleRows.length + visibleRows.length) * (_visibleColumns.length + visibleColumns.length)) + { + var column = unionVisibleColumns.location, + columnEnd = CPMaxRange(unionVisibleColumns), + + rowStart = unionVisibleRows.location, + rowEnd = CPMaxRange(unionVisibleRows); + + for (; column < columnEnd; ++column) + { + var row = rowStart, + tableColumn = _tableColumns[column], + tableColumnCells = _tableCells[column], + columnIsVisible = CPLocationInRange(column, visibleColumns); + + for (; row < rowEnd; ++row) + { + var cell = tableColumnCells[row]; + + if (cell) + { + if (columnIsVisible && CPLocationInRange(row, visibleRows)) + [tableColumn _markView:cell inRow:row asPurgable:NO]; + else { + //!!! + _tableCells[column][row] = nil; + [tableColumn _markView:cell inRow:row asPurgable:YES]; + } + } + + else + { +// ASSERT(CPLocationInRange(row, visibleRows) && CPLocationInRange(column, visibleColumns)) + tableColumnCells[row] = [self newCellForRow:row column:column avoidingRows:visibleRows]; + + [_tableColumnViews[column] addSubview:tableColumnCells[row]]; + } + } } + } + else + { + + } + + _visibleRows = visibleRows; + _visibleColumns = visibleColumns; + + /* + var column = columnStart; + + for (; column < _numberOfColumns && CGRectIntersectsRect([_tableColumnViews[column] frame], aRect); ++column) + { + var row = rowStart, + tableColumn = _tableColumns[column]; + + for (; row < rowEnd; ++row) + { + //if (CPLocationInRange(row, _visibleRows)) + // continue; + + var cell = _tableCells[column][row]; + + if (cell) + [tableColumn _markView:cell inRow:row asPurgable:NO]; + + else + _tableCells[column][row] = [self newCellForRow:row column:column avoidingRows:visibleRows]; + } + } + + var visibleRows = visibleRowsCPMakeRange(rowStart, rowEnd - rowStart), + visibleColumns = CPMakeRange(columnStart, rememberColumn - columnStart); + + var columnEnd = CPMaxRange(_visibelColumns); + + + for (column = _visibleColumns.location; column < columnEnd; ++column) + { + var tableColumn = _tableColumns[tableColumn], + tableColumnCells = _tableCells[column]; + + for (row = _visibleRows.location, rowEnd = CPMaxRange(_visibleRows); row < rowEnd; ++row) + if (!CPLocationInRange(row, visibleRows) || !CPLocationInRange(column, visibleColumns)) + { + var view = tableColumnCells[row]; + + if (view) + [tableColumn _markView:view inRow:row asPurgable:YES]; + } + } + + _visibleRows = visibleRows; + _visibleColumns = visibleColumns;*/ } // Setting display attributes @@ -155,7 +295,7 @@ var CPTableViewCellPlaceholder = nil; for (; i < _numberOfColumns; ++i, total += delta) { var origin = [_tableColumnViews[i] frame].origin; - [_tableColumnViews[i] setFrameOrigin:CPPointMake(origin.x + total, origin.y)]; + [_tableColumnViews[i] setFrameOrigin:CGPointMake(origin.x + total, origin.y)]; } } @@ -165,14 +305,14 @@ var CPTableViewCellPlaceholder = nil; for (; i < _numberOfColumns; ++i, total += delta) { - [_tableColumnViews[i] setFrameSize:CPSizeMake([_tableColumnViews[i] width], _numberOfRows * (_rowHeight + _intercellSpacing.height))]; + [_tableColumnViews[i] setFrameSize:CGSizeMake([_tableColumnViews[i] width], _numberOfRows * (_rowHeight + _intercellSpacing.height))]; var j = 1, y = _rowHeight + _intercellSpacing.height; for (; j < _numberOfRows; ++i, y += _rowHeight + _intercellSpacing.height) { - if (_tableCells[i][j] == CPTableViewCellPlaceholder) + if (!_tableCells[i][j]) continue; [_tableCells[i][j] setFrameOrigin:CPPointMake(0.0, y)]; @@ -227,8 +367,8 @@ var CPTableViewCellPlaceholder = nil; { var i = 0, x = _numberOfColumns ? CPRectGetMaxX([self rectOfColumn:_numberOfColumns - 1]) + _intercellSpacing.width : 0.0, - tableColumnView = [[CPView alloc] initWithFrame:CPRectMake(x, 0.0, [aTableColumn width], [self columnHeight])], - tableColumnCells = [[CPArray alloc] init]; + tableColumnView = [[CPView alloc] initWithFrame:CPRectMake(x, 0.0, [aTableColumn width], [self _columnHeight])], + tableColumnCells = []; [_tableColumns addObject:aTableColumn]; [_tableColumnViews addObject:tableColumnView]; @@ -240,8 +380,8 @@ var CPTableViewCellPlaceholder = nil; [_tableCells addObject:tableColumnCells]; for (; i < _numberOfRows; ++i) - _tableCells[_numberOfColumns][i] = CPTableViewCellPlaceholder; - + _tableCells[_numberOfColumns][i] = nil; + ++_numberOfColumns; } @@ -315,8 +455,8 @@ var CPTableViewCellPlaceholder = nil; { var HEIGHT = 10.0; - [_headerView setFrame:CPRectMake(0.0, 0.0, CPRectGetWidth([self bounds]), HEIGHT)]; - [_scrollView setFrame:CPRectMake(0.0, HEIGHT, CPRectGetWidth([self bounds]), CPRectGetHeight([self bounds]) - HEIGHT)]; +// [_headerView setFrame:CPRectMake(0.0, 0.0, CPRectGetWidth([self bounds]), HEIGHT)]; +// [_scrollView setFrame:CPRectMake(0.0, HEIGHT, CPRectGetWidth([self bounds]), CPRectGetHeight([self bounds]) - HEIGHT)]; } /* @@ -349,25 +489,6 @@ var CPTableViewCellPlaceholder = nil; return _dataSource; } -- (void)setFrameSize:(CGSize)aSize -{ - var oldColumnHeight = [self columnHeight]; - - [super setFrameSize:aSize]; - - var columnHeight = [self columnHeight]; - - if (columnHeight != oldColumnHeight) - { - var i = 0; - - for (; i < _numberOfColumns; ++i) - [_tableColumnViews[i] setFrameSize:CPSizeMake([_tableColumns[i] width], columnHeight)]; - } - - [self tile]; -} - /* Tells the table view that the number of rows in the table has changed. @@ -379,7 +500,8 @@ var CPTableViewCellPlaceholder = nil; if (_numberOfRows != numberOfRows) { _numberOfRows = numberOfRows; - [self sizeToFit]; + + [self _recalculateColumnHeight]; } } @@ -400,7 +522,7 @@ var CPTableViewCellPlaceholder = nil; */ - (CGRect)rectOfColumn:(int)aColumnIndex { - return CPRectCreateCopy([_tableColumnViews[aColumnIndex] frame]); + return [_tableColumnViews[aColumnIndex] frame]; } /* @@ -408,7 +530,35 @@ var CPTableViewCellPlaceholder = nil; */ - (void)sizeToFit { - [self tile]; +// [self tile]; +} + +- (void)_recalculateColumnHeight +{ + var oldColumnHeight = _columnHeight; + + if (_hasVariableHeightRows) + { + } + else + _columnHeight = _numberOfRows * (_rowHeight + _intercellSpacing.height); + + var count = _tableColumnViews.length; + + while (count--) + [_tableColumnViews[count] setFrameSize:CGSizeMake([_tableColumns[count] width], _columnHeight)]; + + [self setFrameSize:CGSizeMake(CGRectGetWidth([self frame]), _columnHeight)]; +} + +- (CGRect)visibleRectInParent +{ + var superview = [self superview]; + + if (!superview) + return [self bounds]; + + return [self convertRect:CGRectIntersection([superview bounds], [self frame]) fromView:superview]; } /* @@ -417,9 +567,60 @@ var CPTableViewCellPlaceholder = nil; */ - (void)reloadData { + var oldNumberOfRows = _numberOfRows; + _numberOfRows = [_dataSource numberOfRowsInTableView:self]; - [self loadTableCellsInRect:[self bounds]]; + if (oldNumberOfRows != _numberOfRows) + { + [self _recalculateColumnHeight]; + [self setFrameSize:CGSizeMake(CGRectGetWidth([self frame]), [self _columnHeight])]; + } + + [self setNeedsDisplay:YES]; +} + +- (void)viewWillDraw +{ + [self loadTableCellsInRect:[self visibleRectInParent]]; + + //alert("oh yes. " + CPStringFromRect([self visibleRect])); + //[self reloadData]; +} + +- (void)drawRect:(CGRect)aRect +{ +} + +- (void)setFrameSize:(CGSize)aFrameSize +{ + [super setFrameSize:aFrameSize]; + +// [self setNeedsDisplay:YES]; +} + +- (void)displaySoon +{ +// window.setTimeout(); +} + +- (void)viewDidMoveToSuperview +{ + [[[self enclosingScrollView] contentView] setPostsBoundsChangedNotifications:YES]; + + [[CPNotificationCenter defaultCenter] + addObserver:self + selector:@selector(viewBoundsChanged:) + name:CPViewBoundsDidChangeNotification + object:[[self enclosingScrollView] contentView]]; + //console.warn("cheese"); +} + +- (void)viewBoundsChanged:(CPNotification)aNotification +{ + //console.warn(_cmd + CPStringFromRect([[[self enclosingScrollView] contentView] bounds])); + //objj_debug_print_backtrace(); + [self setNeedsDisplay:YES]; } @end