Search code examples
cocoadrawingsubclassnsbuttoncell

NSButtonCell subclass' button type not working



I subclassed the NSButtonCell class to give a different look to my button. I have the drawing code working fine. It's a rounded button filled with CGGradients to look like the iTunes playback controls. They are not completely the same, but they resemble enough to me.

Now my problem is the button type. I set the button type in the -[PlayerButtonCell initImageCell:] but I'm not getting it working to only draw the pushed in look when the button is pressed. I present you a piece of my code:

//
//  PlayerButtonCell.m
//  DownTube
//

#import "PlayerButtonCell.h"

@implementation PlayerButtonCell
/* MARK: Init */
- (id)initImageCell:(NSImage *)image {
    self = [super initImageCell:image];
    if(self != nil) {
        [self setImage:image];
        [self setButtonType:NSMomentaryPushInButton];
    }
    return self;
}

- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
    NSImage *myImage;
    NSBezierPath *myPath;
    NSRect myFrame;
    NSInteger myState;

    myFrame = NSInsetRect(cellFrame , STROKE_WIDTH / 2.0 , STROKE_WIDTH / 2.0);
    myImage = [self image];
    myState = [self state];

    NSLog(@"%d", [self buttonType]);

    /* Create bezier path */
    {
        myPath = [NSBezierPath bezierPathWithOvalInRect:myFrame];
    }

    /* Fill with background color */
    {
        /* Fill code here */
    }


    /* Draw gradient if on */
    if(myState == NSOffState) {
        /* Code to draw the button when it's not pushed in */

    } else {
        /* Code to draw the button when it IS pressed */
    }

    /* Stroke */
    {
        /* Code to stroke the bezier path's edge. */
    }
}
@end

As you can see, I check the pushed in status by the [NSButtonCell state] method. But the button cell acts like a check box, as in NSSwitchButton. This, I do not want. I want it to momentary light.

Hope someone can help me,
ief2

EDIT: I create a button with this code of it is of any use:

- (NSButton *)makeRefreshButton {
    NSButton *myButton;
    NSRect myFrame;
    NSImage *myIcon;
    PlayerButtonCell *myCell;

    myIcon = [NSImage imageNamed:kPlayerViewRefreshIconName];
    myCell = [[PlayerButtonCell alloc] initImageCell:myIcon];

    myFrame.origin = NSMakePoint(CONTENT_PADDING , CONTENT_PADDING);
    myFrame.size = CONTROL_PLAYBACK_SIZE;

    myButton = [[NSButton alloc] initWithFrame:myFrame];
    [myButton setCell:myCell];
    [myButton setButtonType:NSMomentaryPushInButton];

    [myCell release];

    return [myButton autorelease];
}

Solution

  • You're looking at the wrong property. -state is whether a button is checked or not. You want to look at isHighlighted, which tells you whether your button is pressed or not.

    Look at a checkbox:

    Checkbox states

    When you click and hold on it, the checkbox is still unchecked, but darkens slightly (702). Then when you release the mouse, it checks (704). Then when you click and hold again, the checked checkbox darkens, but is still checked (705), and only when you release inside, it actually unchecks (701).

    So there are two different aspects, highlight and state, which combine to provide one of 4 appearances. Every NSButtonCell advances its state to whatever -nextState returns whenever it is clicked. But some button types show the state, while others don't. Look at any pushbutton (e.g. the "OK" button in a dialog window) and you'll find that behind the scenes its state is changed between on and off, even though it always draws the same way.

    It is your drawing code that looks at the -showsStateBy property and only shows the state if that indicates it should.