Problem with setting a delegate on a NSView (Cocoa / OSX)


#1

Hi,

I’m trying to add a delegate to a NSView object in Cocoa. I quite a bit of code from glutin by @tomaka (thanks, you will get a proper credits in the doc once done)

The problem is that I can’t get setDelegate to work correctly on an NSView (I need the drawRect callback in order to do custom rendering on the View)

 impl ViewDelegate {
    fn class() -> *const Class {
        use std::sync::{Once, ONCE_INIT};

        extern fn draw_rect(this: &Object, _: Sel, _: id) {
            println!("draw_rect");
        }

        static mut delegate_class: *const Class = 0 as *const Class;
        static INIT: Once = ONCE_INIT;

        INIT.call_once(|| unsafe {
            println!("Create new ViewDelegate");
            let superclass = Class::get("NSObject").unwrap();
            let mut decl = ClassDecl::new(superclass, "minifb_view_delegate").unwrap();

            // Add callback methods
            decl.add_method(sel!(drawRect:),
            draw_rect as extern fn(&Object, Sel, id));
            // Store internal state as user data
            decl.add_ivar::<*mut libc::c_void>("minifb_view_state");

            delegate_class = decl.register();
        });

        unsafe {
            delegate_class
        }
    }

    fn new(state: ViewDelegateState) -> ViewDelegate {
        let mut state = Box::new(state);
        let state_ptr: *mut ViewDelegateState = &mut *state;
        unsafe {
            let delegate = IdRef::new(msg_send![ViewDelegate::class(), new]);

            (&mut **delegate).set_ivar("minifb_view_state", state_ptr as *mut libc::c_void); 
            // BOOM!
            let _: () = msg_send![*state.view, setDelegate:*delegate];

            ViewDelegate { state: state, _this: delegate }
        }
    }
}

When doing the msg_send!(…) here I get

[NSView setDelegate:]: unrecognized selector sent to instance 0x7fe90d132510

The full code can be found here https://github.com/emoon/rust_minifb/blob/mac-rs/src/macos.rs#L133

If anyone is able to help out with this that would be awesome!

Cheers!


#2

I’m no expert on either objc or the Rust binding to it, but is there a reason why you use msg_send! instead setDelegate_ here? You could use the non_nil().map() helper that you use elsewhere. Based on my reading of your code and the macro definition, that should be identical to what you are doing, but perhaps it would help show the problem.

You could also try setting a delegate right after you create the window/view, just for test, to see if it responds to the message.


#3

Hi,

Thanks for the reply!

In this case I actually don’t need to use non_nil.map() as I have already checked that the View is valid before hand here https://github.com/emoon/rust_minifb/blob/mac-rs/src/macos.rs#L232

Also the reason why I’m using msg_send! is that it’s being used in a similar fashion here

From which I based my code for NSView (this code is for NSWindow)

Also I do set set the delegate after the Window/View has been created so I’m not sure what’s going on here and I’m no expert on either Cocoa or ObjC.


#4

Ah, I missed the fact that you were using setDelegate on both the window and the view.

I think one problem here is that NSView objects do not “naturally” support delegates, which is why you are getting the unrecognized selector message. The Rust “NSView” wrapper does not not define setDelegate
( https://github.com/servo/cocoa-rs/blob/master/src/appkit.rs#L1231 ) . That isn’t definitive, because maybe its just missing, but the objective c docs also don’t mention it as a property ( https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSView_Class/ ). Also, after googling around a bit, I see people that are manually adding a delegate field to their custom views, as seen in this SO answer: ( http://stackoverflow.com/questions/6891030/how-does-an-nsview-subclass-communicate-with-the-controller ) So bottom in that I think the issue is with your view implementation and not something specific to Rust, but since I am a total AppKit ignoramus I can’t tell you how to fix it.

Absent other solutions, one approach would be to try getting it to work in objective C then convert that working solution back to Rust.


#5

Thanks a lot for this reply,

I assumed that NSView would work similar to NSWindow in regards to delegates (bad move on my part) :slight_smile:

I will investigate this a bit more and I think your suggestion of trying to get it to work in Objective C first might be a good idea. Also it would be possible to just let this code be in Objective C but it would be nice to have the lib fully written in Rust.

Cheers!