Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Draw grid lines in NSTableView only for populated rows?

Tags:

cocoa

How can I draw grid lines in an NSTableView only for rows that are populated? When I set the grid style mask to NSTableViewSolidHorizontalGridStyleMask, it draws grid lines for all rows whether populated or not.

I'm looking for a bit of sample code to do this.

like image 989
sam Avatar asked Apr 09 '11 17:04

sam


4 Answers

What I found to work best for me so far is the following code. Just fool the original grid drawing code to draw only on populated rows.

Subclass NSTableView, if needed, and override drawGridInClipRect:(NSRect)clipRect as following:

- (void)drawGridInClipRect:(NSRect)clipRect
{
    NSRect lastRowRect = [self rectOfRow:[self numberOfRows]-1];
    NSRect myClipRect = NSMakeRect(0, 0, lastRowRect.size.width, NSMaxY(lastRowRect));
    NSRect finalClipRect = NSIntersectionRect(clipRect, myClipRect);
    [super drawGridInClipRect:finalClipRect];
}
like image 162
Jacob Gorban Avatar answered Nov 16 '22 03:11

Jacob Gorban


In Swift 3.1 this is all that you need:

import Cocoa

class GridClipTableView: NSTableView {

    override func drawGrid(inClipRect clipRect: NSRect) { }

}
like image 32
Oskar Avatar answered Nov 16 '22 04:11

Oskar


I think you will have to subclass NSTableView and override drawRow:clipRect:. Another possibility would be overriding drawGridInClipRect:, but that's a bit more unwieldy, because you'll have to manually pick out the rows.

This should get you going in the right direction:

- (void)drawRow:(NSInteger)rowIndex clipRect:(NSRect)clipRect {
    // First do the default drawing
    [super drawRow:rowIndex clipRect:clipRect];

    // Check if there's content in any of the cells in this row
    BOOL doesHaveContent = NO;
    int numCols = [self numberOfColumns];
    int colIndex;
    for( colIndex = 0; colIndex < numCols; colIndex++ ){
        NSCell * cell = [self preparedCellAtColumn:colIndex 
                                               row:rowIndex];
        if( [cell objectValue] != nil ){
            doesHaveContent = YES;
            break;
        }
    }

    if( doesHaveContent ){
        NSRect rowRect = [self rectOfRow:rowIndex];
        NSBezierPath * gridPath = [NSBezierPath bezierPath];
        // Ignoring clipRect because this isn't a lot of drawing
        [gridPath moveToPoint:rowRect.origin];
        [gridPath lineToPoint:NSMakePoint(rowRect.origin.x + rowRect.size.width,
                                          rowRect.origin.y)];
        [gridPath moveToPoint:NSMakePoint(rowRect.origin.x,
                                          rowRect.origin.y + rowRect.size.height)];
        [gridPath lineToPoint:NSMakePoint(rowRect.origin.x + rowRect.size.width,
                                          rowRect.origin.y + rowRect.size.height)];
        [myGridColor set];
        [gridPath stroke];
        // You could also just do:
        // [myGridColor set];
        // [[NSBezierPath bezierPathWithRect:rowRect] stroke];
    }
}

Yet another possibility would be to have a custom NSCell; each cell could draw a special border around itself if it had content. This wouldn't draw a line across the whole row, though, if there were some cells in the row that were empty.

like image 2
jscs Avatar answered Nov 16 '22 03:11

jscs


Neither solution listed here worked for me, for some reason. What I did instead is:

  1. Subclass NSTableView and override rectOfColumn: (see below).
  2. Turn on layer backing on the table view, its clip view, and its scroll view (this is required for this solution to work).

This worked from macOS 10.9 up to 10.15 in my tests.

- (NSRect)rectOfColumn:(NSInteger)column
{
    NSRect value = [super rectOfColumn:column];

    NSInteger numberOfRows = [self numberOfRows];
    if (numberOfRows == 0) {
        value.size.height = 0;
    } else {
        NSRect firstRect = [self rectOfRow:0];
        NSRect lastRect = [self rectOfRow:numberOfRows - 1];

        value.size.height = NSMaxY(lastRect) - NSMinY(firstRect);
    }

    return value;
}
like image 1
DarkDust Avatar answered Nov 16 '22 05:11

DarkDust