Minimalistic cocoa application

Published: Feb 2, 2011

Some time ago I faced with problem - to write crossplatform application with GUI, but it should not contain any tool-specific resources like MSVC rc files, or xcode resources. It should build GUI in the most native way. Simplify problem: there was the following interface in existing code base, that I have to satisfy.

struct IWindow
{
  virtual void Create() = 0;
  virtual void DoEvents() = 0;
  virtual void Focus() = 0;
  virtual void Hide() = 0;
  virtual void Close() = 0;
  virtual bool IsClosed() = 0;
};

And an application should be designed to use the similar pattern (it comes from windows-os application as far you can see):

int main()
{
  IWindow* window = OSDependent::GetCurrentWindowSystem()->GetWindow();
  windiw->Create();
  window->Focus();
  while (!window->IsClose())
  {
    window->DoEvents();
    // some logic should be placed here
  }
  return 0;
}

#ifdef __OBJC__
class MyWindow : public IWindow
{
public:
  ///!< .. methods of IWindow interface
protected:
  NSWindow* window_;
};
#endif // __OBJC__

After a lot of reading and number of tries, I made a native Cocoa window without any xcode-specific resource. Ta-da-dam!! :) The main function of this post:

void MyWindow::Create()
{
  NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
   
  [NSApplication sharedApplication];
  [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
        
  unsigned int styles = NSResizableWindowMask | 
    NSClosableWindowMask | 
    NSTitledWindowMask;
        
  NSRect rectanle = NSMakeRect(100, 100, 640, 480);
  window_ = [[NSWindow alloc] 
    initWithContentRect:rectangle
    styleMask:styles
    backing:NSBackingStoreBuffered 
    defer:NO];
  [window_ setTitle:@"(none)"];
  [window_ setReleasedWhenClosed:NO];
  [window_ 
    performSelectorOnMainThread:@selector(makeKeyAndOrderFront:) 
    withObject:nil 
    waitUntilDone:YES];
  [NSApp activateIgnoringOtherApps:YES];
  [NSApp finishLaunching];
        
  [pool release];
}

The most strange code here is setActivationPolicy code line. It allows window to be activated, receive keyboard and mouse event and more. I spent a lot of time to find it (this was my first macosx experience).

Next key of system - a message pump. Usually, it isn’t necessary, because of usage [NSApp run]; functionality to make window application work. As far I understand NSApp run includes look-like code. In my case, I have to implement message cycle by myself. Here it is:

void MyWindow::DoEvents()
{
  NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
    untilDate:[NSDate distantPast]
    inMode:NSDefaultRunLoopMode
    dequeue:YES];
  if (event) {
    [NSApp sendEvent:event];
    [NSApp updateWindows];
  }
  [pool release];
}

Other minor function implementation, easy to get, but might be useful:

bool MyWindow::IsClosed() 
{
  return ![window_ isVisible];
}
void MyWindow::Focus()
{
  NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  [window_ makeKeyAndOrderFront:nil];
  [pool release];
}
void MyWindow::Hide()
{
  NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  [window_ orderOut:nil];
  [pool release];
}
void MyWindow::Close()
{
  [window_ close];
}

As for addition, we could add custom NSView to our window, here the defenition of derived view.

@interface MyView : NSView
{
  MyWindow* window_;
}

- (id)initWithFrame:(NSRect)rectangle;
- (id)initWithMyWindow:(MyWindow*)window;
- (MyWindow*)getWindow;
@end

@implementation MyView

- (id)initWithFrame:(NSRect)rectangle
{
  self = [super initWithFrame:rectangle];
  m_Window = 0;
  return self;
}
- (id)initWithMyWindow:(MyWindow*)window
{
  Math::Rect wr = w->GetRectangle();
  NSRect r = NSMakeRect(wr.x, wr.y, wr.Width, wr.Height);
  if (self = [self initWithFrame:r])
  {
    window_ = window;
  }
  return self;
}
- (MyWindow*)getWindow
{
  return window_;
}
- (BOOL)acceptsFirstResponder
{
  return YES;
}
- (BOOL)becomeFirstResponder
{
  return YES;
}
- (BOOL)resignFirstResponder
{
  return YES;
}
- (BOOL)canBecomeKeyView
{
  return YES;
}
@end

Final window create method:

void MyWindow::Create()
{
  NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
        
  [NSApplication sharedApplication];
  [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
        
  unsigned int styles = NSResizableWindowMask | 
    NSClosableWindowMask | 
    NSTitledWindowMask;
        
  NSRect rectanle = NSMakeRect(100, 100, 640, 480);
  window_ = [[NSWindow alloc] 
    initWithContentRect:rectangle
    styleMask:styles
    backing:NSBackingStoreBuffered 
    defer:NO];

  [window_ setTitle:@"(none)"];
        
  [window_ setReleasedWhenClosed:NO];
       
  view_ = [[MyView alloc] initWithMyWindow:this];
  [window_ setContentView:view_];
  [window_ makeFirstResponder:view_];
        
  [view_ setFrame:NSMakeRect(0, 0, rectangle.Width, rectangle.Height)];
       
  [window_ 
    performSelectorOnMainThread:@selector(makeKeyAndOrderFront:) 
    withObject:nil 
    waitUntilDone:YES];
  [NSApp activateIgnoringOtherApps:YES];
  [NSApp finishLaunching];
        
  [pool release];
}

That’s all. Yes, this is not the most minimal cocoa application, but it is created without usage of xcode magic, and includes not-big code :) The most interesting and hard things have been found at cocoa with love.

P.S. Sorry for my bad english ^^