RubyCocoa Sheets - help anyone?

Ok, I am making progress!

It now raises a sheet! horay! however, I can only make it work when the
pannel that I raise as a sheet is held within the same NIB that defines
that main window. With the following code:

ib_outlets :mainWindow, :prefsWindow

(IBAction)

def openPrefsSheet(sender)

NSApp.beginSheet_modalForWindow_modalDelegate_didEndSelector_contextInfo(@prefsWindow, 

@mainWindow, nil, nil, nil)
NSApp.runModalForWindow(@prefsWindow)

NSApp.endSheet(@prefsWindow)
@prefsWindow.orderOut(self)
end

(IBAction)

def closePrefsSheet(sender)
NSApp.stopModal
end

The outlets were then linked up in IB correctly. This all works hunky
dory :slight_smile: However, I would prefer it if the sheets that I load were in
external NIB files to keep things nice and tidy.

Given that when I am using a single NIB I could hook up the sheets using
IB but presumably when using external NIBs i will need to replace this
hooking up in IB with somthing more programatic?

Thanks

Tim

That calls for a drink! (Pours a pint of Guinness…)

hahaha i know! Beers all around…!!!

@mainWindow, nil, nil, nil)
OSX::NSBundle.loadNibNamed_owner (“Prefs”, self)

You include a bit of my begin sheet code here, i guess that needs to
stay pretty much as is, just with the load for the actual sheet
changing?

The key in all this is whether you want your controller object to load
your
NIB, or the NIB to create the controller. If you want the former, you
set
up the “file’s owner” connections. For the latter, you instantiate the
object in IB, and set up the connections to that instead.

You make an interesting point there - the code that is actually doing
this (or will ultimatly be doing this rather) is in a subclass of
NSWindowController - im guessing I cant instansiate the controller
within IB so i’ll have to go for the file owners approach?

How do i set the class as the files owner, do i make it the file owners
delegate connection by dragging the connection in IB?

I havent sat down and tried this 2nd nib version yet, i’ll do it tomorow
AM. What you are saying however is that my controller for any given
window can load in the 2nd nib file, then reference itself as the
provider for that sheet with its newly loaded contents?

Cheers sherm

Tim P. [email protected] writes:

It now raises a sheet! horay!

That calls for a drink! (Pours a pint of Guinness…)

@mainWindow, nil, nil, nil)
OSX::NSBundle.loadNibNamed_owner (“Prefs”, self)

The outlets were then linked up in IB correctly. This all works hunky
dory :slight_smile: However, I would prefer it if the sheets that I load were in
external NIB files to keep things nice and tidy.

That’s where the loadNibNamed_owner() method comes into play. You’d
define
your class in the NIB that holds the sheet, tell IB that the file’s
owner
is an instance of that class, and connect the file’s owner “prefsWindow”
outlet to the sheet.

What you do not want to do is instantiate your Ruby class in
Prefs.nib.
Doing that would result in two instances of your controller object,
one
in each NIB.

Given that when I am using a single NIB I could hook up the sheets using
IB but presumably when using external NIBs i will need to replace this
hooking up in IB with somthing more programatic?

No, your outlets are still connected in IB.

The difference is what you connect them to. It sounds like you’ve
created
an instance of your Ruby class in your main window NIB, which is
represented
by the “blue cube” icon. When that NIB is loaded, Cocoa’s NIB loading
machinery will create a new instance of your class, and then connect the
outlets you’ve defined.

But, if you set the class of the “file’s owner” in IB to your Ruby
class,
Cocoa will not create a new instance when the NIB is loaded. Instead,
it will use the instance that was passed as the second argument to the
loadNibNamed_owner() method, and connect the outlets to that.

The key in all this is whether you want your controller object to load
your
NIB, or the NIB to create the controller. If you want the former, you
set
up the “file’s owner” connections. For the latter, you instantiate the
object in IB, and set up the connections to that instead.

You can also do both. You can instantiate your controller in your main
NIB,
and connect its outlets to the “blue cube” icon in that NIB. Then, you
can
load a secondary NIB using loadNibNamed_owner() - in the secondary NIB,
the
outlets would need to be connected to “file’s owner”.

sherm–

Then select the “file’s owner” and select your Ruby class in the “custom
class” pane of the info panel. Again - this doesn’t create an instance
of
your controller class, it just lets IB know that, when this NIB is
loaded
at run time, what class its owner object will be an instance of. This is
how IB knows what outlets and actions to show in the “connections” pane
of the Info panel.

You make a very good point there sherm that a lot of people including
myself miss.

I have finally got this working!!! wahooooooooooo!!! The sheet
displays correctly and dismisses on the button click :smiley:

As we discussed earlyer in the thread about having the callback using
the didEndSheet_returnCode_contextInfo(sheet, code, context) method,
that is presumably how i pass the input of my displayed sheet into the
controller?

Sorry to bang on - the end is now in sight!!!

Cheers sherm

Tim

Tim P. [email protected] writes:

myself miss.

I have finally got this working!!! wahooooooooooo!!! The sheet
displays correctly and dismisses on the button click :smiley:

As we discussed earlyer in the thread about having the callback using
the didEndSheet_returnCode_contextInfo(sheet, code, context) method,
that is presumably how i pass the input of my displayed sheet into the
controller?

I would actually do that in the action method. For instance, let’s say
that
your sheet has an text field and two buttons - OK and Cancel.

In the OK action method, I’d get the input from the text field and store
it
wherever it needed to go, and call endSheet_returnCode() with a return
code
of 1. In the Cancel action, I’d pass a code of 0.

In the callback, I’d first do an orderOut() on the sheet, then check the
return code. If it’s 1 I’d notify anything that needs to know about the
new
value read from the text field. By storing the changed data immediately
but
delaying the notification until the callback, any visual updates won’t
happen until after the sheet is out of the way - so the user is more
likely
to notice the change.

sherm–

Tim P. [email protected] writes:

changing?
this (or will ultimatly be doing this rather) is in a subclass of
NSWindowController - im guessing I cant instansiate the controller
within IB so i’ll have to go for the file owners approach?

Right. You can’t instantiate a new controller instance in your Prefs
NIB,
because you want to re-use the one that already exists in the main NIB.

How do i set the class as the files owner, do i make it the file owners
delegate connection by dragging the connection in IB?

In your Prefs NIB, create your NSWindowController subclass. (You’re not
really creating a new class, just telling IB about your Ruby class. You
need to do it manually because IB doesn’t grok .rb files.) Be sure to
add
the outlet for the Prefs window you’ll be using as a sheet.

Then select the “file’s owner” and select your Ruby class in the “custom
class” pane of the info panel. Again - this doesn’t create an instance
of
your controller class, it just lets IB know that, when this NIB is
loaded
at run time, what class its owner object will be an instance of. This is
how IB knows what outlets and actions to show in the “connections” pane
of the Info panel.

That’s a key point, worth repeating: You’re not creating any new classes
or instances with these two steps. All you’re doing is making IB aware
of
what you’ve already created in code.

All that’s left is to make the connection to the outlet. Just
control-drag
from the “file’s owner” to the prefs window, select the outlet you want
to
connect it to, and click the “connect” button.

Now, when you call loadNibNamed_owner(), the connections you’ve defined
in
the NIB will be made to the object you pass as the second argument.

What you are saying however is that my controller for any given
window can load in the 2nd nib file, then reference itself as the
provider for that sheet with its newly loaded contents?

I’m not certain what you mean by “provider” here, but I guess you mean
to
say “owner”. Yes, that’s one key difference between the two techniques.
When you instantiate an object inside of a NIB, you can only
control-drag
connections to it from other objects inside that same NIB.

But, when you instantiate your controller in code, you can then pass it
to
as many loadNibNamed_owner() method calls as you’d like. You’ll have one
common controller object whose outlets are connected to objects in as
many
different NIB files as you need; all you need to do is control-drag the
connection in each NIB to the “file’s owner” icon.

sherm–

Tim P. [email protected] writes:

@mainWindow, nil, ‘didEndSheet:returnCode:contextInfo’, nil)
^
You forgot the trailing colon on the selector ---------^

Also, you’re passing nil as the modalDelegate - you should pass the
object
to which you want Cocoa to send the selector - self in this case.

sherm–

Quoting Tim P. [email protected]:

NSApp.endSheet(@prefsWindow)
#@prefsWindow.orderOut(self)
end

def didEndSheet_returnCode_contextInfo(sheet, returnCode, context)
NSLog(‘callback’)
@prefsWindow.orderOut(self)
end

You’re doing things slightly differently to how i usually do this so I
may be slightly off base…

In your begin sheet method your modal delegate is nil so your
didEndSelector is going to be sent to nil.
It should probably be ‘self’. The format of your didEndSelector is
different from how I would expect.
That doesn’t mean it’s wrong but when I do this I make it a symbol the
same as the actual method ie…

:didEndSheet_returnCode_contextInfo

I don’t think you need the runModalForWindow call. That is what your
beginSheet_modalForWindow
method is doing anyway.

Then you are ending the sheet straight away which probably isn’t what
you want either. Calling
endSheet triggers a call to your didEndSelector. You probably want to do
that in response to a user
action eg. ‘OK’ button.

Here is some code for a login sheet from one of my apps. It may or may
not be useful…

def login_sheet
    OSX::NSApp().beginSheet_modalForWindow_modalDelegate_didEndSelector_contextInfo

(@login_sheet, @main_window, self, :login_button_returnCode_contextInfo,
nil)
end

def login_button_returnCode_contextInfo sheet, returnCode, 

contextInfo
sheet.orderOut nil
finish_upload
end

def end_login_sheet sender
    @login = @login_field.stringValue
    @password = @password_field.stringValue

    OSX::NSApp().endSheet_returnCode @login_sheet, 0
end

def cancel_login_sheet sender
    OSX::NSApp().endSheet_returnCode @login_sheet, 0
end

PS These sorts of questions might be better directed at the RubyCocoa
mailing list…

[email protected] writes:

The format of your didEndSelector is different from how I would expect.
That doesn’t mean it’s wrong but when I do this I make it a symbol the
same as the actual method ie…

:didEndSheet_returnCode_contextInfo

Ah, that could be a problem, yes. I’ve done plenty of Cocoa in
Objective-C
and Perl, but I’m more of a newbie with RubyCocoa. I wasn’t aware that
it
would bridge method references into selectors like that.

Come to think of it, I may just have to steal that idea for CamelBones.
:slight_smile:

sherm–

For sure, that sounds like a very logical way of doing things.

Here is my code to call the sheet…

def displaySheet(sender)
NSLog(‘opening sheet’)

NSBundle.loadNibNamed_owner(‘Prefs’, self)
NSApp.beginSheet_modalForWindow_modalDelegate_didEndSelector_contextInfo(@prefsWindow,
@mainWindow, nil, ‘didEndSheet:returnCode:contextInfo’, nil)

creates modal session, sheet is displayed at this point

NSApp.runModalForWindow(@prefsWindow)

NSApp.endSheet(@prefsWindow)
#@prefsWindow.orderOut(self)
end

def didEndSheet_returnCode_contextInfo(sheet, returnCode, context)
NSLog(‘callback’)
@prefsWindow.orderOut(self)
end

But now, with the callback signiture in, it crasheses when ever it goes
to close the sheet? I have played around with it and it just bongs out
whatever you seem to do?

Any ideas?

Cheers

Tim

Hey Guys

Its not working---- horayyy!!!

Thanks to everyone who chipped in, especially sherm who kept comming
back over and over! top quality :slight_smile:

I’ll send the code to rubycocoa.com, Tim might stick it on the site as
its not covered anywhere else

Cheers

Tim

oops - I meant to say “its now working!!!”

LOL - a rather appropriate end to this thread!

Cheers

Tim

Sherm P. wrote:

[email protected] writes:

The format of your didEndSelector is different from how I would expect.
That doesn’t mean it’s wrong but when I do this I make it a symbol the
same as the actual method ie…

:didEndSheet_returnCode_contextInfo

Ah yes, now that is good! It makes sense to do that and is very
ruby-esq, should have tried this really!

I am going to try all these usefull suggestions today so when i get
somthing working im going to ping it over to Tim Burkes to stick on the
main RubyCocoa site - with any luck this should help somone else
struggling with sheets!

Tim