1

I have a function which creates an instance of button, and I'd like the callback to go to a separate function. This doesn't work (no error or anything, just nothing happens when I click on the button). I need it to be a C function because I'm interfacing with Golang (using cgo).

void Start(void){
    ...
    NSRect buttonFrame = NSMakeRect(59, 33, 82, 32);
    NSButton *button = [[NSButton alloc] initWithFrame:buttonFrame];
    [button setAction:@selector(ButtonClick:)];
    ...
}
void ButtonClick(void){
    NSLog(@"Button clicked!");
}
lucasem
  • 481
  • 3
  • 12
  • Just a thought: try adding `[button setTarget:self];` above the 5th line in your snippet. – CorbinMc Dec 25 '14 at 02:05
  • undeclared identifier `self`. What I've written here shows *everything*, minus making a window etc. and running it (which is done within `Start`). – lucasem Dec 25 '14 at 02:06
  • Is button click a function? If so the first letter should be lowercase. That *might* be the issue. – CorbinMc Dec 25 '14 at 02:16
  • `ButtonClick` is declared just below `Start`... I tried changing `ButtonClick` to `buttonClick` (both in the function declaration and in `setAction`), and the result is still the same (nothing happens when I click on the button). – lucasem Dec 25 '14 at 02:19
  • Did you make the connection between buttonClick and your NSButton using XCode's interface or did you type it manually? – CorbinMc Dec 25 '14 at 02:22
  • This is just a C file, complied and run with certain flags. No Xcode (because I'm interfacing with Golang using cgo, not that it matters). Everything I've done relating to the button I've shown in the prompt's code. – lucasem Dec 25 '14 at 02:23
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/67648/discussion-between-lucasem-and-corbinmc). – lucasem Dec 25 '14 at 02:27
  • 4
    You can't use a function for this. The button has to have a target which is an object, on which a method with the appropriate name is available. – jscs Dec 25 '14 at 02:35
  • 1
    This isn't a C file; you're sending messages (the square bracket syntax), so you must be compiling as ObjC. – jscs Dec 25 '14 at 02:39
  • @lucasem Why are you expecting Cocoa, an Objective-C framework, to work like a C library? It is NOT a C library. It doesn't work in the way you want and continuing to insist it has to work this way will not get you a solution to your problem. – mttrb Dec 25 '14 at 02:40
  • 1
    If you need to call a C function when the button is clicked because you want to bridge to Golang you will have to get the button to call a method which then calls the C function. – mttrb Dec 25 '14 at 02:47
  • You should be getting a compiler warning about using an unknown selector name. That's should be a hint that you're doing something wrong. – jscs Dec 25 '14 at 02:53
  • @JoshCaswell There is no compiler warning, it works fine and I can see my window, button, etc. I use golang and, before `#import `, I set some flags: `#cgo CFLAGS: -x objective-c`; `#cgo LDFLAGS: -framework Cocoa`. – lucasem Dec 25 '14 at 03:45

2 Answers2

4

You cannot use a C function as the action for an NSButton. The button requires a target which is an object and the selector for a method on that target. If there's no target, there must still be an object in your window's responder chain that will respond to the selector.

You must create an object (doesn't have to be an instance; you can use a class object if you so choose) in order for the button to operate. The method also needs to have a particular signature: it must take one argument, which will be the button when it's called.

If you must use the function you've already written, you will have to write an ObjC class that calls through to it from the action method:

#import <Cocoa/Cocoa.h>

@interface ButtonPasser : NSObject

+ (IBAction)buttonPassthrough:(id)sender;

@end

@implementation ButtonPasser

+ (IBAction)buttonPassthrough:(id)sender
{
    buttonClick();
}

@end

void start(void){
    ...
    NSRect buttonFrame = NSMakeRect(59, 33, 82, 32);
    NSButton *button = [[NSButton alloc] initWithFrame:buttonFrame];
    [button setTarget:[ButtonPasser class]];
    [button setAction:@selector(buttonPassthrough:)];
    ...
}

This uses the class object and a class method, since I'm not sure what you would do with an instance after you created it. Using an instance would be much more usual, however.

jscs
  • 63,694
  • 13
  • 151
  • 195
  • Objects are instances, correct? I've never heard of a "class object". – CorbinMc Dec 25 '14 at 02:54
  • 1
    Classes are objects in ObjC: http://stackoverflow.com/questions/6469770/class-objects-and-instance-variables-in-objective-c/6474371#6474371 – jscs Dec 25 '14 at 02:55
  • Nice, @matt! I'm going to use that to replace the broken link in my answer. Thanks! – jscs Dec 25 '14 at 03:01
  • Thank you! As a follow-up (after which I will give you the check-off) what would a simple object look like for this, along with setting it as the target for my button? – lucasem Dec 25 '14 at 03:43
  • A target of `nil` is not a no-op. It targets the responder chain. Although an action will eventually be delivered using something like `-performSelector:withObject:`, that's not the immediate call made by the button. The button will do `[NSApp sendAction:self.action to:self.target from:self]` and the application will search for the target if it's `nil`. – Ken Thomases Dec 25 '14 at 07:05
  • Thanks for the correction, @KenThomases. I've revised. – jscs Dec 25 '14 at 07:25
1

Your problem is that neither your Start or ButtonClick methods (should be start and buttonClick) are instance methods. They both must be tied to an object in order for buttonClick to be set as an action.

Once these are both instance methods you should be able to add [button setTarget:self]; above your setAction call.

If you cannot make start an instance method, you should add another method in between your calls.

CorbinMc
  • 588
  • 2
  • 8
  • As I've said in the prompt, for what I'm trying to do, I need a _function_ (globally, something like `void f(void){}`) to run, __not__ a method of some object. – lucasem Dec 25 '14 at 02:33
  • This is not the proper way to implement a button. Button actions must be sent to an object's instance method. – CorbinMc Dec 25 '14 at 02:37
  • 1
    Cocoa is an Objective-C framework. It does not work with static functions. If you want to capture the click on a button it must be to an Objective-C method. I really don't understand why you expect an Objective-C framework to work like a C library. – mttrb Dec 25 '14 at 02:37