diff --git a/AppKit/CPTableHeaderView.j b/AppKit/CPTableHeaderView.j index d726457ab..edb914c1c 100644 --- a/AppKit/CPTableHeaderView.j +++ b/AppKit/CPTableHeaderView.j @@ -905,7 +905,7 @@ var CPTableHeaderViewResizeZone = 3.0, [_tableView _loadDataViewsInRows:rowsIndexes columns:columnsIndexes]; [_tableView _layoutViewsForRowIndexes:rowsIndexes columnIndexes:columnsIndexes]; - + [_tableView _updateDataViewsFocusState]; [_tableView._tableDrawView displayRect:columnVisRect]; [[CPCursor arrowCursor] set]; // FIXME: retirer ? diff --git a/AppKit/CPTableView.j b/AppKit/CPTableView.j index 0d1850ba1..e1793e9f2 100644 --- a/AppKit/CPTableView.j +++ b/AppKit/CPTableView.j @@ -172,33 +172,110 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; @end -@implementation _CPTableDrawView : CPView +// Internal class to handle row background, selection and grid lines via DOM/CSS +// This replaces the Canvas-based _CPTableDrawView to avoid coordinate limits. +@implementation _CPTableRowView : CPView { - id _tableView; + CPInteger _rowIndex; + CPTableView _tableView; + CPView _gridLineView; + BOOL _selected; + BOOL _focused; } - (id)initWithTableView:(CPTableView)aTableView { - self = [super init]; - + self = [super initWithFrame:CGRectMakeZero()]; if (self) + { _tableView = aTableView; + _selected = NO; + _focused = NO; + // Optimization: Turn off hit testing for the background row + // The CPTableView handles clicks, or the data views on top handle them. + [self setHitTests:NO]; + } return self; } -- (void)drawRect:(CGRect)aRect +- (void)setRowIndex:(CPInteger)anIndex { - var frame = [self frame], - context = [[CPGraphicsContext currentContext] graphicsPort]; + _rowIndex = anIndex; + [self updateState]; +} - CGContextTranslateCTM(context, -CGRectGetMinX(frame), -CGRectGetMinY(frame)); +- (void)setSelected:(BOOL)isSelected focused:(BOOL)isFocused +{ + if (_selected === isSelected && _focused === isFocused) + return; - [_tableView _drawRect:aRect]; + _selected = isSelected; + _focused = isFocused; + [self updateState]; +} + +- (void)updateState +{ + var backgroundColor = nil; + + if (_selected && [_tableView selectionHighlightStyle] != CPTableViewSelectionHighlightStyleNone) + { + if (_focused) + backgroundColor = [_tableView selectionHighlightColor]; + else + backgroundColor = [_tableView unfocusedSelectionHighlightColor]; + } + else if ([_tableView usesAlternatingRowBackgroundColors]) + { + var colors = [_tableView alternatingRowBackgroundColors]; + if ([colors count] > 0) + backgroundColor = colors[_rowIndex % [colors count]]; + } + + // Fallback if no color decided (transparent/white) + if (!backgroundColor) + backgroundColor = [CPColor clearColor]; + + [self setBackgroundColor:backgroundColor]; + [self _updateGridLine]; +} + +- (void)_updateGridLine +{ + var mask = [_tableView gridStyleMask]; + + if (mask & CPTableViewSolidHorizontalGridLineMask) + { + if (!_gridLineView) + { + _gridLineView = [[CPView alloc] initWithFrame:CGRectMakeZero()]; + [_gridLineView setBackgroundColor:[_tableView gridColor]]; + // Ensure grid line is at the bottom of the row view + [_gridLineView setAutoresizingMask:CPViewWidthSizable | CPViewMinYMargin]; + [self addSubview:_gridLineView]; + } + + var thickness = [_tableView valueForThemeAttribute:@"grid-line-thickness"] || 1.0; + [_gridLineView setFrame:CGRectMake(0, CGRectGetHeight([self bounds]) - thickness, CGRectGetWidth([self bounds]), thickness)]; + [_gridLineView setHidden:NO]; + } + else + { + [_gridLineView setHidden:YES]; + } +} + +- (void)resizeWithOldSuperviewSize:(CGSize)oldSize +{ + [super resizeWithOldSuperviewSize:oldSize]; + if (_gridLineView && ![_gridLineView isHidden]) + [self _updateGridLine]; } @end + /*! @ingroup appkit @class CPTableView @@ -248,6 +325,10 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; CPDictionary _archivedDataViews; Object _unavailable_custom_cibs; + // Row View Management (DOM Composition) + Object _visibleRowViews; + CPArray _cachedRowViews; + //Configuring Behavior BOOL _allowsColumnReordering; BOOL _allowsColumnResizing; @@ -293,8 +374,6 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; CPInteger _editingRow; CPInteger _editingColumn; - _CPTableDrawView _tableDrawView; - SEL _doubleAction; CPInteger _clickedRow; CPInteger _clickedColumn; @@ -420,7 +499,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; _destinationDragStyle = CPTableViewDraggingDestinationFeedbackStyleRegular; _contentBindingExplicitlySet = NO; - [self setBackgroundColor:[self currentValueForThemeAttribute:@"background-color"]]; + // [self setBackgroundColor:[CPColor whiteColor]]; [self _init]; } @@ -466,9 +545,9 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; _groupRows = [CPIndexSet indexSet]; - _tableDrawView = [[_CPTableDrawView alloc] initWithTableView:self]; - [_tableDrawView setBackgroundColor:[CPColor clearColor]]; - [self addSubview:_tableDrawView]; + // Row View Initialization for DOM rendering + _visibleRowViews = { }; + _cachedRowViews = []; _draggedColumnIndex = -1; _draggedColumnIsSelected = NO; @@ -496,11 +575,19 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; _BlockDeselectView = function(view, row, column) { [view unsetThemeState:CPThemeStateSelectedDataView]; + // Ensure we clear the focus state so the text goes back to normal color + [view unsetThemeState:CPThemeStateFirstResponder]; }; _BlockSelectView = function(view, row, column) { [view setThemeState:CPThemeStateSelectedDataView]; + + // If the table is focused, apply the state that turns text white immediately + if ([self _isFocused]) + [view setThemeState:CPThemeStateFirstResponder]; + else + [view unsetThemeState:CPThemeStateFirstResponder]; }; } @@ -666,7 +753,8 @@ NOT YET IMPLEMENTED { [self _setupReload]; [self setNeedsLayout]; - [self setNeedsDisplay:YES]; + // No more manual draw rect + // [self setNeedsDisplay:YES]; } // Reloads the views AND the data @@ -869,6 +957,7 @@ NOT YET IMPLEMENTED - (void)setUsesAlternatingRowBackgroundColors:(BOOL)shouldUseAlternatingRowBackgroundColors { _usesAlternatingRowBackgroundColors = shouldUseAlternatingRowBackgroundColors; + [self _updateAllVisibleRowViews]; } /*! @@ -887,8 +976,7 @@ NOT YET IMPLEMENTED - (void)setAlternatingRowBackgroundColors:(CPArray)alternatingRowBackgroundColors { [self setValue:alternatingRowBackgroundColors forThemeAttribute:@"alternating-row-colors"]; - - [self setNeedsDisplay:YES]; + [self _updateAllVisibleRowViews]; } /*! @@ -899,20 +987,6 @@ NOT YET IMPLEMENTED return [self currentValueForThemeAttribute:@"alternating-row-colors"]; } -- (void)setThemeState:(CPThemeState)aState -{ - [super setThemeState:aState]; - // Force update the background color from the theme - [self setBackgroundColor:[self currentValueForThemeAttribute:@"background-color"]]; -} - -- (void)unsetThemeState:(CPThemeState)aState -{ - [super unsetThemeState:aState]; - // Force update the background color from the theme - [self setBackgroundColor:[self currentValueForThemeAttribute:@"background-color"]]; -} - /*! Returns an enumerated value for the selection highlight style. @@ -951,7 +1025,7 @@ NOT YET IMPLEMENTED [self _updateHighlightWithOldRows:[CPIndexSet indexSet] newRows:_selectedRowIndexes]; [self _updateHighlightWithOldColumns:[CPIndexSet indexSet] newColumns:_selectedColumnIndexes]; - [self setNeedsDisplay:YES]; + [self _updateAllVisibleRowViews]; } /*! @@ -965,7 +1039,7 @@ NOT YET IMPLEMENTED return; [self setValue:aColor forThemeAttribute:@"selection-color"]; - [self setNeedsDisplay:YES]; + [self _updateAllVisibleRowViews]; } /*! @@ -982,7 +1056,14 @@ NOT YET IMPLEMENTED - (CPColor)unfocusedSelectionHighlightColor { if (!_unfocusedSelectionHighlightColor) - _unfocusedSelectionHighlightColor = [self hasThemeAttribute:@"unfocused-selection-color"] ? [self currentValueForThemeAttribute:@"unfocused-selection-color"] : [self _unfocusedSelectionColorFromColor:[self selectionHighlightColor] saturation:0]; + { + // Check theme attributes first. + // This ensures 'unfocused-selection-color' from ThemeDescriptors.j is used. + _unfocusedSelectionHighlightColor = [self currentValueForThemeAttribute:@"unfocused-selection-color"]; + + if (!_unfocusedSelectionHighlightColor) + _unfocusedSelectionHighlightColor = [self _unfocusedSelectionColorFromColor:[self selectionHighlightColor] saturation:0]; + } return _unfocusedSelectionHighlightColor; } @@ -1072,8 +1153,7 @@ NOT YET IMPLEMENTED - (void)setGridColor:(CPColor)aColor { [self setValue:aColor forThemeAttribute:@"grid-color"]; - - [self setNeedsDisplay:YES]; + [self _updateAllVisibleRowViews]; } /*! @@ -1095,8 +1175,7 @@ NOT YET IMPLEMENTED return; _gridStyleMask = aGrideStyleMask; - - [self setNeedsDisplay:YES]; + [self _updateAllVisibleRowViews]; } /*! @@ -1364,8 +1443,9 @@ NOT YET IMPLEMENTED _selectedColumnIndexes = [columns copy]; [self _updateHighlightWithOldColumns:previousSelectedIndexes newColumns:_selectedColumnIndexes]; - [self setNeedsDisplay:YES]; // FIXME: should be setNeedsDisplayInRect:enclosing rect of new (de)selected columns - // but currently -drawRect: is not implemented here + // No more setNeedsDisplay for drawing + // [self setNeedsDisplay:YES]; + if (_headerView) [_headerView setNeedsDisplay:YES]; @@ -1386,8 +1466,6 @@ NOT YET IMPLEMENTED _selectedRowIndexes = [rows copy]; [self _updateHighlightWithOldRows:previousSelectedIndexes newRows:_selectedRowIndexes]; - [self setNeedsDisplay:YES]; // FIXME: should be setNeedsDisplayInRect:enclosing rect of new (de)selected rows - // but currently -drawRect: is not implemented here var binderClass = [[self class] _binderClassForBinding:@"selectionIndexes"]; [[binderClass getBinding:@"selectionIndexes" forObject:self] reverseSetValueFor:@"selectedRowIndexes"]; @@ -1470,6 +1548,18 @@ NOT YET IMPLEMENTED if (_selectionHighlightStyle !== CPTableViewSelectionHighlightStyleNone) [self _enumerateViewsInRows:newRows columns:_exposedColumns usingBlock:_BlockSelectView]; + + // Update Row Views (DOM Backgrounds) + var focused = [self _isFocused]; + [oldRows enumerateIndexesUsingBlock:function(idx, stop) { + var rowView = _visibleRowViews[idx]; + if (rowView) [rowView setSelected:NO focused:focused]; + }]; + + [newRows enumerateIndexesUsingBlock:function(idx, stop) { + var rowView = _visibleRowViews[idx]; + if (rowView) [rowView setSelected:YES focused:focused]; + }]; } /*! @@ -2197,6 +2287,9 @@ NOT YET IMPLEMENTED { [super resizeWithOldSuperviewSize:aSize]; + // Ensure all row views stretch to fill the new width + [self _updateAllVisibleRowViewsFrames]; + if (_disableAutomaticResizing) return; @@ -2336,13 +2429,22 @@ NOT YET IMPLEMENTED // so we should size the last resized to fit // find the last visisble column - while (count-- && [_tableColumns[count] isHidden]); + while (count--) + { + var hidden = [_tableColumns[count] isHidden]; + if (!hidden) + break; + } // find the max x, but subtract a single pixel since the spacing isn't applicable here. - var delta = superviewWidth - CGRectGetMaxX([self rectOfColumn:count]) - ([self intercellSpacing].width || 1), - newSize = [item width] + delta; + if (count >= 0) + { + var item = _tableColumns[count], + delta = superviewWidth - CGRectGetMaxX([self rectOfColumn:count]) - ([self intercellSpacing].width || 1), + newSize = [item width] + delta; - [item _tryToResizeToWidth:newSize]; + [item _tryToResizeToWidth:newSize]; + } } /*! @@ -2524,7 +2626,8 @@ NOT YET IMPLEMENTED [self setFrameSize:CGSizeMake(width, height)]; [self setNeedsLayout]; - [self setNeedsDisplay:YES]; + // No more display needed for layout + // [self setNeedsDisplay:YES]; } @@ -3144,16 +3247,18 @@ Your delegate can implement this method to avoid subclassing the tableview to ad return dragView; } -- (CPView)_dragViewForColumn:(CPInteger)columnIndex -{ - return [self _makeGhostViewForColumn:columnIndex forDragging:YES]; -} +// CPTableView.j - (CPView)_animationViewForColumn:(CPInteger)columnIndex { return [self _makeGhostViewForColumn:columnIndex forDragging:NO]; } +- (CPView)_dragViewForColumn:(CPInteger)columnIndex +{ + return [self _makeGhostViewForColumn:columnIndex forDragging:YES]; +} + - (CPView)_makeGhostViewForColumn:(CPInteger)columnIndex forDragging:(BOOL)isForDragging { var headerFrame = [_headerView frame], @@ -3198,7 +3303,6 @@ Your delegate can implement this method to avoid subclassing the tableview to ad [self _enumerateViewsInRows:_exposedRows columns:[CPIndexSet indexSetWithIndex:columnIndex] usingBlock:function(dataView, row, column, stop) { [self _addDraggedDataView:dataView toView:columnClipView forColumn:column row:row offset:offset]; - delete (_dataViewsForRows[row][tableColumnUID]); }]; @@ -3234,7 +3338,6 @@ Your delegate can implement this method to avoid subclassing the tableview to ad dataViewFrame.origin.y -= offset.y; [aDataView setFrame:dataViewFrame]; - [aSuperview addSubview:aDataView]; } @@ -3496,9 +3599,10 @@ Your delegate can implement this method to avoid subclassing the tableview to ad _exposedRows = exposedRows; _exposedColumns = exposedColumns; - [_tableDrawView setFrame:exposedRect]; + // Remove old draw view call + // [_tableDrawView setFrame:exposedRect]; - [self setNeedsDisplay:YES]; + // [self setNeedsDisplay:YES]; // Not needed for structure changes // if we have any columns to remove do that here @@ -3546,6 +3650,17 @@ Your delegate can implement this method to avoid subclassing the tableview to ad */ - (void)_unloadDataViewsInRows:(CPIndexSet)rowIndexes columns:(CPIndexSet)columnIndexes { + // Recycle Row Views + [rowIndexes enumerateIndexesUsingBlock:function(rowIndex, stop) { + var rowView = _visibleRowViews[rowIndex]; + if (rowView) + { + [rowView removeFromSuperview]; + _cachedRowViews.push(rowView); + delete _visibleRowViews[rowIndex]; + } + }]; + if (![rowIndexes count] || ![columnIndexes count] || [columnIndexes lastIndex] >= [_tableColumns count]) return; @@ -3572,6 +3687,33 @@ Your delegate can implement this method to avoid subclassing the tableview to ad */ - (void)_loadDataViewsInRows:(CPIndexSet)rowIndexes columns:(CPIndexSet)columnIndexes { + // Load Row Views + var focused = [self _isFocused], + visibleWidth = MAX([self bounds].size.width, CGRectGetWidth([self exposedRect])); + + [rowIndexes enumerateIndexesUsingBlock:function(rowIndex, stop) { + if (!_visibleRowViews[rowIndex]) + { + var rowView = [self _dequeueRowView]; + if (!rowView) + rowView = [[_CPTableRowView alloc] initWithTableView:self]; + + [rowView setRowIndex:rowIndex]; + [rowView setSelected:[self isRowSelected:rowIndex] focused:focused]; + + // Set frame to span the entire width + var rowRect = [self _rectOfRow:rowIndex checkRange:NO]; + rowRect.size.width = visibleWidth; + rowRect.origin.x = 0; + + [rowView setFrame:rowRect]; + + // Add behind data views + [self addSubview:rowView positioned:CPWindowBelow relativeTo:nil]; + _visibleRowViews[rowIndex] = rowView; + } + }]; + if (![rowIndexes count] || ![columnIndexes count]) return; @@ -3606,6 +3748,38 @@ Your delegate can implement this method to avoid subclassing the tableview to ad }]; } +- (CPView)_dequeueRowView +{ + if (_cachedRowViews.length > 0) + return _cachedRowViews.pop(); + return nil; +} + +- (void)_updateAllVisibleRowViews +{ + var focused = [self _isFocused]; + for (var idx in _visibleRowViews) + { + if (_visibleRowViews.hasOwnProperty(idx)) + [_visibleRowViews[idx] setSelected:[self isRowSelected:+idx] focused:focused]; + } +} + +- (void)_updateAllVisibleRowViewsFrames +{ + var visibleWidth = MAX([self bounds].size.width, CGRectGetWidth([self exposedRect])); + for (var idx in _visibleRowViews) + { + if (_visibleRowViews.hasOwnProperty(idx)) + { + var rowView = _visibleRowViews[idx], + rect = [rowView frame]; + rect.size.width = visibleWidth; + [rowView setFrame:rect]; + } + } +} + - (CPView)preparedViewAtColumn:(CPInteger)column row:(CPInteger)row { return [self _preparedViewAtColumn:column row:row isRowSelected:[self isRowSelected:row]]; @@ -3622,10 +3796,20 @@ Your delegate can implement this method to avoid subclassing the tableview to ad [self _setObjectValueForTableColumn:tableColumn row:row forView:dataView]; if (_selectionHighlightStyle !== CPTableViewSelectionHighlightStyleNone && (isRowSelected || [self isColumnSelected:column])) + { _BlockSelectView(dataView, row, column); - else - _BlockDeselectView(dataView, row, column); + if ([self _isFocused]) + [dataView setThemeState:CPThemeStateFirstResponder]; + else + [dataView unsetThemeState:CPThemeStateFirstResponder]; + } + else + { + _BlockDeselectView(dataView, row, column); + // Ensure we clear this state if a view is reused + [dataView unsetThemeState:CPThemeStateFirstResponder]; + } // FIX ME: for performance reasons we might consider diverging from cocoa and moving this to the reloadData method if (_implementedDelegateMethods & CPTableViewDelegate_tableView_isGroupRow_) { @@ -3644,7 +3828,11 @@ Your delegate can implement this method to avoid subclassing the tableview to ad } if (!_isViewBased) - [self _setEditingState:NO forView:dataView]; + { + // We use the 'row' and 'column' arguments here to check against the instance variables + var isEditing = (row === _editingRow && column === _editingColumn); + [self _setEditingState:isEditing forView:dataView]; + } [self _sendDelegateWillDisplayView:dataView forTableColumn:tableColumn row:row]; @@ -3689,6 +3877,18 @@ Your delegate can implement this method to avoid subclassing the tableview to ad { [view setFrame:[self frameOfDataViewAtColumn:column row:row]]; }]; + + // Also layout row views if needed (usually handled by resize) + var visibleWidth = MAX([self bounds].size.width, CGRectGetWidth([self exposedRect])); + [rowIndexes enumerateIndexesUsingBlock:function(row, stop) { + if (_visibleRowViews[row]) + { + var rect = [self _rectOfRow:row checkRange:NO]; + rect.size.width = visibleWidth; + rect.origin.x = 0; + [_visibleRowViews[row] setFrame:rect]; + } + }]; } /*! @@ -4021,7 +4221,6 @@ Your delegate can implement this method to avoid subclassing the tableview to ad - (void)setNeedsDisplay:(BOOL)aFlag { [super setNeedsDisplay:aFlag]; - [_tableDrawView setNeedsDisplay:aFlag]; [[self headerView] setNeedsDisplay:YES]; } @@ -4048,331 +4247,33 @@ Your delegate can implement this method to avoid subclassing the tableview to ad */ - (void)_drawRect:(CGRect)aRect { - // FIX ME: All three of these methods will likely need to be rewritten for 1.0 - // We've got grid drawing in highlightSelection and crap everywhere. - - var exposedRect = [self exposedRect]; - - [self drawBackgroundInClipRect:exposedRect]; - [self highlightSelectionInClipRect:exposedRect]; - [self drawGridInClipRect:exposedRect]; - + // NO-OP: We no longer draw on canvas. DOM composition handles background, grid and selection. if (_implementsCustomDrawRow) - [self _drawRows:_exposedRows clipRect:exposedRect]; + [self _drawRows:_exposedRows clipRect:aRect]; } /*! Draws the background in a given clip rect. - This method should only be overridden if you want something other than a solid color or alternating row colors. - @note this method should not be called directly, instead use \c setNeedsDisplay: + Deprecated: Handled by _CPTableRowView */ - (void)drawBackgroundInClipRect:(CGRect)aRect { - if (!_usesAlternatingRowBackgroundColors) - return; - - var rowColors = [self alternatingRowBackgroundColors], - colorCount = [rowColors count]; - - if (colorCount === 0) - return; - - var context = [[CPGraphicsContext currentContext] graphicsPort]; - - if (colorCount === 1) - { - CGContextSetFillColor(context, rowColors[0]); - CGContextFillRect(context, aRect); - - return; - } - - var exposedRows = [self _exposedRowsInRect:aRect], - firstRow = FLOOR(exposedRows.location / colorCount) * colorCount, - lastRow = CPMaxRange(exposedRows) - 1, - colorIndex = 0, - groupRowRects = []; - - //loop through each color so we only draw once for each color - while (colorIndex < colorCount) - { - CGContextBeginPath(context); - - for (var row = firstRow + colorIndex; row <= lastRow; row += colorCount) - { - // if it's not a group row draw it otherwise we draw it later - if (![_groupRows containsIndex:row]) - CGContextAddRect(context, CGRectIntersection(aRect, [self _rectOfRow:row checkRange:NO])); - else - groupRowRects.push(CGRectIntersection(aRect, [self _rectOfRow:row checkRange:NO])); - } - - CGContextClosePath(context); - - CGContextSetFillColor(context, rowColors[colorIndex]); - CGContextFillPath(context); - - colorIndex++; - } - - [self _drawGroupRowsForRects:groupRowRects]; } /*! - Draws the grid for the tableview based on the set grid mask in a given clip rect. - @note this method should not be called directly, instead use setNeedsDisplay: + Draws the grid for the tableview. + Deprecated: Handled by _CPTableRowView */ - (void)drawGridInClipRect:(CGRect)aRect { - var context = [[CPGraphicsContext currentContext] graphicsPort], - gridStyleMask = [self gridStyleMask], - lineThickness = [self currentValueForThemeAttribute:@"grid-line-thickness"]; - - if (!(gridStyleMask & (CPTableViewSolidHorizontalGridLineMask | CPTableViewSolidVerticalGridLineMask))) - return; - - CGContextBeginPath(context); - - if (gridStyleMask & CPTableViewSolidHorizontalGridLineMask) - { - var exposedRows = [self _exposedRowsInRect:aRect], - row = exposedRows.location, - lastRow = CPMaxRange(exposedRows) - 1, - rowY = -lineThickness / 2, - minX = CGRectGetMinX(aRect), - maxX = CGRectGetMaxX(aRect); - - for (; row <= lastRow; ++row) - { - // grab each row rect and add the top and bottom lines - var rowRect = [self _rectOfRow:row checkRange:NO], - rowY = CGRectGetMaxY(rowRect) - lineThickness / 2; - - CGContextMoveToPoint(context, minX, rowY); - CGContextAddLineToPoint(context, maxX, rowY); - } - - if (_rowHeight > 0.0) - { - var rowHeight = FULL_ROW_HEIGHT(), - totalHeight = CGRectGetMaxY(aRect) - lineThickness / 2; - - while (rowY < totalHeight) - { - rowY += rowHeight; - - CGContextMoveToPoint(context, minX, rowY); - CGContextAddLineToPoint(context, maxX, rowY); - } - } - } - - if (gridStyleMask & CPTableViewSolidVerticalGridLineMask) - { - var exposedColumnIndexes = [self columnIndexesInRect:aRect], - columnsArray = []; - - [exposedColumnIndexes getIndexes:columnsArray maxCount:-1 inIndexRange:nil]; - - var columnArrayIndex = 0, - columnArrayCount = columnsArray.length, - minY = CGRectGetMinY(aRect), - maxY = CGRectGetMaxY(aRect); - - for (; columnArrayIndex < columnArrayCount; ++columnArrayIndex) - { - var columnRect = [self rectOfColumn:columnsArray[columnArrayIndex]], - columnX = CGRectGetMaxX(columnRect) - lineThickness / 2; - - CGContextMoveToPoint(context, columnX, minY); - CGContextAddLineToPoint(context, columnX, maxY); - } - } - - CGContextClosePath(context); - CGContextSetStrokeColor(context, [self gridColor]); - CGContextSetLineWidth(context, lineThickness); - CGContextStrokePath(context); } /*! - Draws the selection with the set selection highlight style in a given clip rect. - You can change the highlight style to a source list style gradient in setSelectionHighlightStyle: - @note this method should not be called directly, instead use \c setNeedsDisplay: + Draws the selection. + Deprecated: Handled by _CPTableRowView */ - (void)highlightSelectionInClipRect:(CGRect)aRect { - if (_selectionHighlightStyle === CPTableViewSelectionHighlightStyleNone) - return; - - var context = [[CPGraphicsContext currentContext] graphicsPort], - indexes = [], - rectSelector = @selector(rectOfRow:); - - if ([_selectedRowIndexes count] >= 1) - { - var exposedRows = [CPIndexSet indexSetWithIndexesInRange:[self rowsInRect:aRect]], - firstRow = [exposedRows firstIndex], - exposedRange = CPMakeRange(firstRow, [exposedRows lastIndex] - firstRow + 1); - - [_selectedRowIndexes getIndexes:indexes maxCount:-1 inIndexRange:exposedRange]; - } - else if ([_selectedColumnIndexes count] >= 1) - { - rectSelector = @selector(rectOfColumn:); - - var exposedColumns = [self columnIndexesInRect:aRect], - firstColumn = [exposedColumns firstIndex], - exposedRange = CPMakeRange(firstColumn, [exposedColumns lastIndex] - firstColumn + 1); - - [_selectedColumnIndexes getIndexes:indexes maxCount:-1 inIndexRange:exposedRange]; - } - - var count, - count2 = count = [indexes count]; - - if (!count) - return; - - var drawGradient = (CPFeatureIsCompatible(CPHTMLCanvasFeature) && _selectionHighlightStyle === CPTableViewSelectionHighlightStyleSourceList && [_selectedRowIndexes count] >= 1), - deltaHeight = 0.5 * (_gridStyleMask & CPTableViewSolidHorizontalGridLineMask), - focused = [self _isFocused]; - - CGContextBeginPath(context); - - if (drawGradient) - { - var gradientCache = focused ? [self selectionGradientColors] : [self unfocusedSelectionGradientColors], - topLineColor = [gradientCache objectForKey:CPSourceListTopLineColor], - bottomLineColor = [gradientCache objectForKey:CPSourceListBottomLineColor], - gradientColor = [gradientCache objectForKey:CPSourceListGradient]; - } - - var normalSelectionHighlightColor = focused ? [self selectionHighlightColor] : [self unfocusedSelectionHighlightColor]; - - // don't do these lookups if there are no group rows - if ([_groupRows count]) - { - var topGroupLineColor = [CPColor colorWithCalibratedWhite:212.0 / 255.0 alpha:1.0], - bottomGroupLineColor = [CPColor colorWithCalibratedWhite:185.0 / 255.0 alpha:1.0], - gradientGroupColor = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), [212.0 / 255.0, 212.0 / 255.0, 212.0 / 255.0, 1.0, 197.0 / 255.0, 197.0 / 255.0, 197.0 / 255.0, 1.0], [0, 1], 2); - } - - while (count--) - { - var currentIndex = indexes[count], - rowRect = CGRectIntersection(self.isa.objj_msgSend1(self, rectSelector, currentIndex), aRect); - - // group rows get the same highlight style as other rows if they're source list... - if (!drawGradient) - var shouldUseGroupGradient = [_groupRows containsIndex:currentIndex]; - - if (drawGradient || shouldUseGroupGradient) - { - var minX = CGRectGetMinX(rowRect), - minY = CGRectGetMinY(rowRect), - maxX = CGRectGetMaxX(rowRect), - maxY = CGRectGetMaxY(rowRect) - deltaHeight; - - if (!drawGradient) - { - //If there is no source list gradient we need to close the selection path and fill it now - [normalSelectionHighlightColor setFill]; - CGContextClosePath(context); - CGContextFillPath(context); - CGContextBeginPath(context); - } - - CGContextAddRect(context, rowRect); - - CGContextDrawLinearGradient(context, (shouldUseGroupGradient) ? gradientGroupColor : gradientColor, rowRect.origin, CGPointMake(minX, maxY), 0); - - CGContextBeginPath(context); - CGContextMoveToPoint(context, minX, minY + .5); - CGContextAddLineToPoint(context, maxX, minY + .5); - CGContextSetStrokeColor(context, (shouldUseGroupGradient) ? topGroupLineColor : topLineColor); - CGContextStrokePath(context); - - CGContextBeginPath(context); - CGContextMoveToPoint(context, minX, maxY - .5); - CGContextAddLineToPoint(context, maxX, maxY - .5); - CGContextSetStrokeColor(context, (shouldUseGroupGradient) ? bottomGroupLineColor : bottomLineColor); - CGContextStrokePath(context); - } - else - { - var radius = [self currentValueForThemeAttribute:@"selection-radius"]; - - if (radius > 0) - { - var minX = CGRectGetMinX(rowRect), - maxX = CGRectGetMaxX(rowRect), - minY = CGRectGetMinY(rowRect), - maxY = CGRectGetMaxY(rowRect); - - CGContextMoveToPoint(context, minX + radius, minY); - CGContextAddArcToPoint(context, maxX, minY, maxX, minY + radius, radius); - CGContextAddArcToPoint(context, maxX, maxY, maxX - radius, maxY, radius); - CGContextAddArcToPoint(context, minX, maxY, minX, maxY - radius, radius); - CGContextAddArcToPoint(context, minX, minY, minX + radius, minY, radius); - } - else - CGContextAddRect(context, rowRect); - } - } - - CGContextClosePath(context); - - if (!drawGradient) - { - [normalSelectionHighlightColor setFill]; - CGContextFillPath(context); - } - - CGContextBeginPath(context); - - var gridStyleMask = [self gridStyleMask]; - - for (var i = 0; i < count2; i++) - { - var rect = self.isa.objj_msgSend1(self, rectSelector, indexes[i]), - minX = CGRectGetMinX(rect) - 0.5, - maxX = CGRectGetMaxX(rect) - 0.5, - minY = CGRectGetMinY(rect) - 0.5, - maxY = CGRectGetMaxY(rect) - 0.5; - - if ([_selectedRowIndexes count] >= 1 && gridStyleMask & CPTableViewSolidVerticalGridLineMask) - { - var exposedColumns = [self columnIndexesInRect:aRect], - exposedColumnIndexes = [], - firstExposedColumn = [exposedColumns firstIndex], - exposedRange = CPMakeRange(firstExposedColumn, [exposedColumns lastIndex] - firstExposedColumn + 1); - - [exposedColumns getIndexes:exposedColumnIndexes maxCount:-1 inIndexRange:exposedRange]; - - var exposedColumnCount = [exposedColumnIndexes count]; - - for (var c = firstExposedColumn; c < exposedColumnCount; c++) - { - var colRect = [self rectOfColumn:exposedColumnIndexes[c]], - colX = CGRectGetMaxX(colRect) + 0.5; - - CGContextMoveToPoint(context, colX, minY); - CGContextAddLineToPoint(context, colX, maxY); - } - } - - //if the row after the current row is not selected then there is no need to draw the bottom grid line white. - if ([indexes containsObject:indexes[i] + 1]) - { - CGContextMoveToPoint(context, minX, maxY); - CGContextAddLineToPoint(context, maxX, maxY); - } - } - - CGContextClosePath(context); - CGContextSetStrokeColor(context, [self currentValueForThemeAttribute:@"highlighted-grid-color"]); - CGContextStrokePath(context); } /*! @@ -4382,49 +4283,7 @@ Your delegate can implement this method to avoid subclassing the tableview to ad */ - (void)_drawGroupRowsForRects:(CPArray)rects { - if ((CPFeatureIsCompatible(CPHTMLCanvasFeature) && _selectionHighlightStyle === CPTableViewSelectionHighlightStyleSourceList) || !rects.length) - return; - - var context = [[CPGraphicsContext currentContext] graphicsPort], - i = rects.length; - - CGContextBeginPath(context); - - var gradientCache = [self selectionGradientColors], - topLineColor = [CPColor colorWithHexString:"d3d3d3"], - bottomLineColor = [CPColor colorWithHexString:"bebebd"], - gradientColor = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), [220.0 / 255.0, 220.0 / 255.0, 220.0 / 255.0, 1.0, - 199.0 / 255.0, 199.0 / 255.0, 199.0 / 255.0, 1.0], [0, 1], 2), - drawGradient = YES; - - while (i--) - { - var rowRect = rects[i]; - - CGContextAddRect(context, rowRect); - - if (drawGradient) - { - var minX = CGRectGetMinX(rowRect), - minY = CGRectGetMinY(rowRect), - maxX = CGRectGetMaxX(rowRect), - maxY = CGRectGetMaxY(rowRect); - - CGContextDrawLinearGradient(context, gradientColor, rowRect.origin, CGPointMake(minX, maxY), 0); - - CGContextBeginPath(context); - CGContextMoveToPoint(context, minX, minY); - CGContextAddLineToPoint(context, maxX, minY); - CGContextSetStrokeColor(context, topLineColor); - CGContextStrokePath(context); - - CGContextBeginPath(context); - CGContextMoveToPoint(context, minX, maxY); - CGContextAddLineToPoint(context, maxX, maxY - 1); - CGContextSetStrokeColor(context, bottomLineColor); - CGContextStrokePath(context); - } - } + // TODO: Implement Group Row styling in _CPTableRowView } /*! @@ -4486,7 +4345,8 @@ Your delegate can implement this method to avoid subclassing the tableview to ad { _exposedRect = nil; - [self setNeedsDisplay:YES]; + // No need to setNeedsDisplay for canvas + // [self setNeedsDisplay:YES]; [self setNeedsLayout]; } @@ -5082,6 +4942,7 @@ Your delegate can implement this method to avoid subclassing the tableview to ad */ - (void)becomeKeyWindow { + [self _notifyViewDidBecomeFirstResponder]; [self setNeedsDisplay:YES]; } @@ -5090,6 +4951,7 @@ Your delegate can implement this method to avoid subclassing the tableview to ad */ - (void)resignKeyWindow { + [self _notifyViewDidResignFirstResponder]; [self setNeedsDisplay:YES]; } @@ -5367,6 +5229,10 @@ Your delegate can implement this method to avoid subclassing the tableview to ad [self scrollRowToVisible:rowIndex]; [self scrollColumnToVisible:columnIndex]; + // Force layout to update exposed rows/views after scrolling + // This prevents the view from being unloaded/reloaded during the makeFirstResponder call + [self layoutIfNeeded]; + // TODO Do something with flag. _editingRow = rowIndex; @@ -5557,6 +5423,41 @@ Your delegate can implement this method to avoid subclassing the tableview to ad [self scrollRowToVisible:i]; } +- (void)_updateDataViewsFocusState +{ + var isFocused = [self _isFocused]; + + // enumerateAvailableViewsUsingBlock calls [self reloadData], which is destructive + // during a focus change (causes editing session to reset). + [self _enumerateViewsInRows:_exposedRows columns:_exposedColumns usingBlock:function(view, row, column, stop) + { + // Only selected rows need to toggle between White and Black text + if ([self isRowSelected:row] || [self isColumnSelected:column]) + { + if (isFocused) + [view setThemeState:CPThemeStateFirstResponder]; + else + [view unsetThemeState:CPThemeStateFirstResponder]; + } + }]; +} + +- (void)_notifyViewDidBecomeFirstResponder +{ + // Ensure the row highlights turn "blue" (active) + [self _updateAllVisibleRowViews]; + // Ensure the text turns white + [self _updateDataViewsFocusState]; +} + +- (void)_notifyViewDidResignFirstResponder +{ + // Ensure the row highlights turn "gray" (inactive) + [self _updateAllVisibleRowViews]; + // Ensure the text turns black + [self _updateDataViewsFocusState]; +} + @end @@ -5897,7 +5798,7 @@ Your delegate can implement this method to avoid subclassing the tableview to ad /* @ignore Returns YES if the column at columnIndex can be reordered. - It can be possible if column reordering is allowed and if the tableview + It can be possible if column reordering is allowed and if the table view delegate also accept the reordering */ - (BOOL)_sendDelegateShouldReorderColumn:(CPInteger)columnIndex toColumn:(CPInteger)newColumnIndex @@ -6150,7 +6051,6 @@ var CPTableViewDataSourceKey = @"CPTableViewDataSourceKey", CPTableViewGridStyleMaskKey = @"CPTableViewGridStyleMaskKey", CPTableViewUsesAlternatingBackgroundKey = @"CPTableViewUsesAlternatingBackgroundKey", CPTableViewAlternatingRowColorsKey = @"CPTableViewAlternatingRowColorsKey", - CPTableViewHeaderViewKey = @"CPTableViewHeaderViewKey", CPTableViewCornerViewKey = @"CPTableViewCornerViewKey", CPTableViewAutosaveNameKey = @"CPTableViewAutosaveNameKey", CPTableViewArchivedReusableViewsKey = @"CPTableViewArchivedReusableViewsKey"; @@ -6192,9 +6092,6 @@ var CPTableViewDataSourceKey = @"CPTableViewDataSourceKey", _headerView = [aCoder decodeObjectForKey:CPTableViewHeaderViewKey]; _cornerView = [aCoder decodeObjectForKey:CPTableViewCornerViewKey]; - // Fix _cornerView height to match _headerView height - [_cornerView setFrameSize:CGSizeMake([CPScroller scrollerWidth], CGRectGetHeight([_headerView frame]))]; - [self setDataSource:[aCoder decodeObjectForKey:CPTableViewDataSourceKey]]; [self setDelegate:[aCoder decodeObjectForKey:CPTableViewDelegateKey]]; @@ -6218,11 +6115,6 @@ var CPTableViewDataSourceKey = @"CPTableViewDataSourceKey", { [super encodeWithCoder:aCoder]; - // We do this in order to avoid encoding the _tableDrawView, which - // should just automatically be created programmatically as needed. - if (_tableDrawView) - [_tableDrawView removeFromSuperview]; - [aCoder encodeObject:_dataSource forKey:CPTableViewDataSourceKey]; [aCoder encodeObject:_delegate forKey:CPTableViewDelegateKey]; @@ -6373,11 +6265,11 @@ var CPTableViewDataSourceKey = @"CPTableViewDataSourceKey", @implementation _CPColumnDragDrawingView : CPView { - CPTableView tableView @accessors; - int columnIndex @accessors; - CPView columnClipView @accessors; - BOOL shouldDrawBorders @accessors; - BOOL isSelected @accessors; + CPTableView tableView @accessors; + int columnIndex @accessors; + CPView columnClipView @accessors; + BOOL shouldDrawBorders @accessors; + BOOL isSelected @accessors; } - (void)drawRect:(CGRect)dirtyRect diff --git a/AppKit/Themes/Aristo3/ThemeDescriptors.j b/AppKit/Themes/Aristo3/ThemeDescriptors.j index 6855499a5..14741e463 100644 --- a/AppKit/Themes/Aristo3/ThemeDescriptors.j +++ b/AppKit/Themes/Aristo3/ThemeDescriptors.j @@ -7187,6 +7187,7 @@ var themedButtonValues = nil, columnsIndexes = [CPIndexSet indexSetWithIndex:aFromIndex]; [theTableView _loadDataViewsInRows:rowsIndexes columns:columnsIndexes]; + [theTableView _updateDataViewsFocusState]; [theTableView _layoutViewsForRowIndexes:rowsIndexes columnIndexes:columnsIndexes]; [theTableView._tableDrawView displayRect:columnVisRect]; @@ -7203,6 +7204,7 @@ var themedButtonValues = nil, animatedHeader = [animatedColumn headerView], animatedHeaderOrigin = [animatedHeader frameOrigin]; + [[s tableView] _updateDataViewsFocusState]; [CPAnimationContext beginGrouping]; var context = [CPAnimationContext currentContext]; @@ -7212,6 +7214,7 @@ var themedButtonValues = nil, [context setCompletionHandler:function() { [s _finalize_stopDraggingTableColumn:aColumnIndex]; + [[s tableView] _updateDataViewsFocusState]; }]; [[_columnDragView animator] setFrameOrigin:CGPointMake(animatedHeaderOrigin.x, 0)];