Using Interface Builder and Nib files with Rococoa

This is a brief tutorial about how to create a user interface in Interface Builder and then load and use that interface in Java, utilizing Rococoa.

Disclaimer: This example runs on my computer. It took a couple of hours to put together. If you try to build a whole application this way, you will probably find a lot more problems to solve and it may not work at all.

The Nib File

For the demo, create a simple Nib file in Interface Builder (File->New…, Choose “Application”). Save (in Nib format) as Hello World.nib:

Nib

Add some content to the main window - a label and a button to click:

Window

Add a single outlet, called mainWindow and a single action, called doIt: to the File’s Owner:

Outlets

Connect the outlet to the NSWindow and the action of the “Hello World” button to the doIt: action.

Connections

The code

The main demo class follows. Compile and run this:

        
    package nibdemo;

    import java.awt.Toolkit;
    import org.rococoa.ID;
    import org.rococoa.Rococoa;
    import org.rococoa.cocoa.NSAutoreleasePool;
    import org.rococoa.cocoa.NSBundle;
    import org.rococoa.cocoa.NSDictionary;
    import org.rococoa.cocoa.NSNib;
    import org.rococoa.cocoa.NSString;
    import org.rococoa.cocoa.NSURL;
    import org.rococoa.cocoa.NSWindow;

    public class DemoNibLoading {
    
        public DemoNibLoading() throws InterruptedException {
            NSAutoreleasePool pool = NSAutoreleasePool.new_();
            NSURL url = NSURL.CLASS.fileURLWithPath("src/main/HelloWorld.nib"); //[1]
            System.out.println("Loading nib from: " + url.absoluteURL());
            NSDictionary filesOwner = NSDictionary.dictionaryWithObjectsAndKeys(Rococoa.proxy(this), NSString.stringWithString(NSNib.NS_NIB_OWNER), null); //[2]
            NSBundle.CLASS.loadNibFile_externalNameTable_withZone(url.path(), filesOwner, null);
            pool.release();
            synchronized(this) {wait();}  //[3]
        }
  
        public static void main(String... argv) throws InterruptedException {
            Toolkit.getDefaultToolkit(); //[4]
            new DemoNibLoading();
        }


        public void setMainWindow(NSWindow mainWindow) {
            System.out.println("Outlet set to: " + mainWindow.title());
        }
        
        public void doIt(ID sender) {
            System.out.println("Hello World from: " + sender);
        }
    }
        
        

Notes

  1. Adjust this path to reflect where you save your Nib file
  2. In a conventional application, for the main Nib file, one would pass NSApp, a global reference to an instance of NSApplication here. This could be achieved in Rococoa by calling +[NSApplication sharedApplication] and passing in the result. For simplicity’s sake in this demo DemoNibLoading is passed as File’s Owner for the main Nib file, even though it is not a subclass of NSApplication. This does not appear to cause any problems in this simple demo. For helpful advice on structuring your nib files you should read Nib Files in Apple’s Resource Programming Guide.
  3. To prevent the Java VM from exiting, the main thread of the Java application will wait() forever on this line. One could alternatively create and show an offscreen JFrame.
  4. This line will cause the AWT to load, which appears to create all of the necessary plumbing for the GUI to work. Maintaining an offscreen JFrame would probably work well here too.

Missing Rococoa Classes?

If you are looking in Rococoa for classes such as NSBundle, NSWindow etc., or for some of the methods used above, be aware that they do not all exist. I will be seeing if I can get the missing items checked in soon. In the meantime they are trivial to create for yourself. e.g. NSWindow:

        
    package org.rococoa.cocoa;

    import org.rococoa.NSClass;
    import org.rococoa.NSObject;
    import org.rococoa.Rococoa;
    
    public interface NSWindow extends NSObject {
        public static final _Class CLASS = Rococoa.createClass(NSWindow.class.getSimpleName(),  _Class.class); //$NON-NLS-1$
    
        public interface _Class extends NSClass {
            public abstract NSWindow alloc();
        }
    
        public String title();
    }