Subrat
Subrat

Reputation: 175

Checkbox in NSTableView column header

I need to add one checkbox in NSTableView column header. i am able to add checkbox in all rows for the above column. but i need at the column header level. i need to do a selectall functionality, but unable to add one checkbox at table header level. Any sample code or ideas will be helpful.

Thanks, Subrat

Upvotes: 4

Views: 2465

Answers (2)

Jonathan Mitchell
Jonathan Mitchell

Reputation: 1367

Using a custom NSCell instance doesn't work for the simple reason that the NSTableHeaderView that co-ordinates the NSHeaderCell instances handles all of the mouse event processing. Hence cell buttons in table headers never respond to mouse events.

The solution is to add the required NSControl instances to a custom NSTableHeaderView. The custom subclass can be set on the table view in IB. Controls are added to identified table columns like so:

[(BPTableHeaderView *)self.tableView.headerView addSubview:self.passwordTableHeaderButton
                                          columnIdentifier:@"password"
                                                 alignment:NSLayoutAttributeRight];

Any NSView instance can be added to a column header though is likely that adding an NSControl instance will be most common.

NSTableHeaderView subclass.

@interface BPTableHeaderView : NSTableHeaderView

- (void)addSubview:(NSView *)view columnIdentifier:(NSString *)identifier alignment:(NSLayoutAttribute)alignment;

@end

@interface BPTableHeaderView()

// collections
@property (strong) NSMutableDictionary<NSString *, NSDictionary *> *store;

// primitives
@property (assign, nonatomic) BOOL subviewsVisible;

@end

@implementation BPTableHeaderView

- (id)initWithFrame:(NSRect)frameRect
{
    self = [super initWithFrame:frameRect];
    if (self) {
        [self commonInit];
    }
    return self;
}

- (id)initWithCoder:(NSCoder *)coder
{
    self = [super initWithCoder:coder];
    if (self) {
         [self commonInit];
    }
    return self;
}

- (void)commonInit
{
    _subviewsVisible = YES;
    _store = [NSMutableDictionary new];
}

#pragma mark -
#pragma mark Accessors

- (void)setSubviewsVisible:(BOOL)subviewsVisible
{
    if (_subviewsVisible == subviewsVisible) return;

    _subviewsVisible = subviewsVisible;

    for (NSString *identifier in self.store.allKeys) {
        NSDictionary *info = self.store[identifier];
        NSView *view = info[@"view"];
        view.hidden = !_subviewsVisible;
    }
}

#pragma mark -
#pragma mark Drawing

- (void)drawRect:(NSRect)dirtyRect {

    [super drawRect:dirtyRect];

    if (self.draggedColumn != -1 || self.resizedColumn != -1) {
        [self layoutSubviews];
    }
}

#pragma mark -
#pragma mark View management

- (void)addSubview:(NSView *)view columnIdentifier:(NSString *)identifier alignment:(NSLayoutAttribute)alignment
{
    self.store[identifier] = @{@"view" : view, @"alignment" : @(alignment)};
    [self addSubview:view];
    self.needsLayout = YES;
}

#pragma mark -
#pragma mark Layout

- (void)layout
{
    [super layout];
    [self layoutSubviews];
}

- (void)layoutSubviews
{
    for (NSString *identifier in self.store.allKeys)
    {
        // info
        NSDictionary *info = self.store[identifier];
        NSView *view = info[@"view"];
        NSLayoutAttribute alignment = [info[@"alignment"] integerValue];

        // views and cells
        NSTableColumn *column = [self.tableView tableColumnWithIdentifier:identifier];
        NSTableHeaderCell *headerCell = column.headerCell;
        NSInteger idx = [self.tableView.tableColumns indexOfObject:column];
        if (idx == NSNotFound) continue;

        // rects
        NSRect headerRect = [self headerRectOfColumn:idx];
        NSRect sortIndicatorRect = NSZeroRect;
        if (column.sortDescriptorPrototype) {
            sortIndicatorRect = [headerCell sortIndicatorRectForBounds:headerRect];
        }

        // position view
        NSPoint viewOrigin = NSMakePoint(0, 0);
        CGFloat y = (headerRect.size.height - view.frame.size.height)/2;
        CGFloat xDelta = 3;
        if (alignment == NSLayoutAttributeLeft) {
            viewOrigin = NSMakePoint(headerRect.origin.x + xDelta, y);
        }
        else {
            viewOrigin = NSMakePoint(headerRect.origin.x + headerRect.size.width - view.frame.size.width - sortIndicatorRect.size.width - 5, y);
        }
        [view setFrameOrigin:viewOrigin];
    }

}
@end

Upvotes: 1

Parag Bafna
Parag Bafna

Reputation: 22930

Create subclass of NSButtonCell

@interface CheckboxHeaderCell : NSButtonCell {
    NSButtonCell *cellCheckBox;
    NSColor *bkColor;
}

-(void)setTitle:(NSString *)title;
-(void)setBkColor:(NSColor *)color;
-(BOOL)getState;
-(void)onClick;

@end  

@implementation CheckboxHeaderCell

- (id)init
{
    if (self = [super init])
    {
        bkColor = nil;

        cellCheckBox = [[ NSButtonCell alloc] init];
        [cellCheckBox setTitle:@""];
        [cellCheckBox setButtonType:NSSwitchButton];
        [cellCheckBox setBordered:NO];
        [cellCheckBox setImagePosition:NSImageRight];
        [cellCheckBox setAlignment:NSLeftTextAlignment];
        [cellCheckBox setObjectValue:[NSNumber numberWithInt:0]];

        [cellCheckBox setControlSize:NSSmallControlSize];
        [cellCheckBox setFont:[NSFont systemFontOfSize:[NSFont
                                                        smallSystemFontSize]]];
    }
    return self;
}

- (void)dealloc
{
    [cellCheckBox release];
    [bkColor release];
    [super dealloc];
}

-(void)setTitle:(NSString *)title
{
    [cellCheckBox setTitle:title];
}

-(void)setBkColor:(NSColor *)color
{
    [color retain];
    [bkColor release];
    bkColor = color;
}

-(BOOL)getState
{
    return [[cellCheckBox objectValue] boolValue];
}

-(void)onClick
{
    BOOL state = ![[cellCheckBox objectValue] boolValue];
    [cellCheckBox setObjectValue:[NSNumber numberWithBool:state]];
}

- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
    if (bkColor != nil)
        [cellCheckBox setBackgroundColor:bkColor];

    [cellCheckBox drawWithFrame:cellFrame inView:controlView] ;
}

@end 

How to use:

CheckboxHeaderCell *mHeaderCell = [[CheckboxHeaderCell alloc] init];
    NSTableColumn *checkBoxColumn = [mOutlineView tableColumnWithIdentifier:@"state"];
    [checkBoxColumn setHeaderCell:mHeaderCell];  

- (void)tableView: (NSTableView *)tableView didClickTableColumn:(NSTableColumn *)tableColumn
{
    NSLog(@"didClickTableColumn");
    CheckboxHeaderCell *headerCell = [tableColumn headerCell];
    [headerCell onClick];
}

Upvotes: 2

Related Questions