Custom View

Gorm can work on the custom view, which is designed by the programmer, not the built-in GUI component. Here, I continue my "time machine" application to demonstrate how to do that. The new interface looks like this:

Figure 12.12. TimeMachine with custom view

TimeMachine with custom view

Since I want to use the custom view in Gorm, I have to design the class first. The custom view can inherit from NSView, or NSControl, depending on what kind of functions you want. Actually, NSControl is a subclass of NSView. So I will inherit from NSControl. Click on the small circle of NSResponder to open its subclasses, then do the same thing on NSView and NSControl. Now, you can see that many GUI component inherit from NSControl, ex. NSTextField.

Figure 12.13. NSControl in Gorm

NSControl in Gorm

I want my custom view, called "TimeView", inherit from NSControl. Choose "NSControl", then select menu "Classes->Create Subclass...". Double-click to change the name.

Figure 12.14. Add subclass of NSControl

Add subclass of NSControl

You can notice that class "TimeView" also inherits 3 outlets and 7 actions from NSControl. Once the class "TimeView" is created, I can use it as custom view.

Build the interface as below:

Figure 12.15. Interface with custom view

Interface with custom view

Look at the "Attributes" in inspector of CustomView. Choose the class "TimeView".

Figure 12.16. Change class of custom view

Change class of custom view

The "CustomView" becomes "TimeView". That's it !

Figure 12.17. Custom view with TimeView class

Custom view with TimeView class

As I did before, create another class for the "controller". Add one outlet for this "TimeView", and one action for the button. I name the outlet "timeView".

Figure 12.18. Add outlet

Add outlet

Figure 12.19. Add action

Add action

Create an instance of class "Controller". Connect the button to the action "showCurrentTime", and the outlet "timeView" to the custom view "TimeView".

Figure 12.20. Connect outlet

Connect outlet
Connect outlet
Connect outlet

Finally, create the class file for the classes "TimeView" and "Controller". Save this application as "TimeMachine.gorm".

Now, I need to design the interface for the class "TimeView". The class "TimeView" is actually four NSTextField in a NSBox. The reason that I made them in one class is because I can reuse it later on. Classes inherited from NSView will be initialized by calling method -initWithFrame:. Therefore, I only need to rewrite the method -initWithFrame: in the class "TimeView". Here are the files:

TimeView.h:

#import <AppKit/AppKit.h>

@interface TimeView : NSControl
{
   NSTextField *labelDate, *labelTime;
   NSTextField *localDate, *localTime;
   NSCalendarDate *date;
}

- (NSCalendarDate *) date;
- (void) setDate: (NSCalendarDate *) date;

@end

TimeView.m:

#import <AppKit/AppKit.h>
#import "TimeView.h"

@implementation TimeView
- (id) initWithFrame: (NSRect) frame
{
   NSBox *box;

   self = [super initWithFrame: frame];
   box = [[NSBox alloc] initWithFrame: NSMakeRect(0, 0,
                                                  frame.size.width,
                                                  frame.size.height)];
   [box setBorderType: NSGrooveBorder];
   [box setTitlePosition: NSAtTop];
   [box setTitle: @"Local Time"];

   labelDate = [[NSTextField alloc] initWithFrame: NSMakeRect(10, 45, 35, 20)];
   [labelDate setStringValue: @"Date: "];
   [labelDate setBezeled: NO];
   [labelDate setBackgroundColor: [NSColor windowBackgroundColor]];
   [labelDate setEditable: NO];

   labelTime = [[NSTextField alloc] initWithFrame: NSMakeRect(10, 15, 35, 20)];
   [labelTime setStringValue: @"Time: "];
   [labelTime setBezeled: NO];
   [labelTime setBackgroundColor: [NSColor windowBackgroundColor]];
   [labelTime setEditable: NO];

   localDate = [[NSTextField alloc] initWithFrame: NSMakeRect(55, 45, 130, 20)];
   localTime = [[NSTextField alloc] initWithFrame: NSMakeRect(55, 15, 130, 20)];

   [box addSubview: labelDate];
   [box addSubview: labelTime];
   [box addSubview: localDate];
   [box addSubview: localTime];
   RELEASE(labelDate);
   RELEASE(labelTime);
   RELEASE(localDate);
   RELEASE(localTime);

   [self addSubview: box];
   RELEASE(box);

   return self;
}

- (NSCalendarDate *) date
{
   return date;
}

- (void) setDate: (NSCalendarDate *) aDate
{
   ASSIGN(date, aDate);
   [date setCalendarFormat: @"%a, %b %e, %Y"];
   [localDate setStringValue: [date description]];
   [date setCalendarFormat: @"%H : %M : %S"];
   [localTime setStringValue: [date description]];
}

- (void) dealloc
{
  RELEASE(date);
  [super dealloc];
}

@end

When Gorm generates the class files, it contains some default codes in it. Since I don't need any of them, they are safe to be removed. In TimeView.h, I declare four NSTextField for display, and on NSCalendarDate to store the date. I also declare two accessory methods to set and get the date. In the -initWithFrame, one NSBox is set up, and four NSTextField is put into it. And class "TimeView" is a subclass of NSView, I add the NSBox as the subview of the class "TimeView".

Other parts of this application should be very easy. Here are the files:

Controller.h:

#import <AppKit/AppKit.h>
#import "TimeView.h"

@interface Controller : NSObject
{
   id timeView;
}
- (void) showCurrentTime: (id)sender;
@end

Controller.m:

#import <AppKit/AppKit.h>
#import "Controller.h"

@implementation Controller
- (void) showCurrentTime: (id)sender
{
   /* insert your code here */
   NSCalendarDate *date = [NSCalendarDate date];
   [timeView setDate: date];
}

@end

main.m:

#import <AppKit/AppKit.h>

int main(int argc, const char *argv[]) 
{
   return NSApplicationMain (argc, argv);
}

GNUmakefile:

include $(GNUSTEP_MAKEFILES)/common.make

APP_NAME = TimeMachine
TimeMachine_HEADERS = Controller.h TimeView.h
TimeMachine_OBJC_FILES = main.m Controller.m TimeView.m
TimeMachine_RESOURCE_FILES = TimeMachineInfo.plist TimeMachine.gorm
TimeMachine_MAIN_MODEL_FILE = TimeMachine.gorm

include $(GNUSTEP_MAKEFILES)/application.make

You should notice that I didn't instantiate the class "TimeView" in class "Controller" because when I add an custom view to the window, it is instantiated automatically. I only need to specify the class the custom view should be. On the contrary, I have to instantiate the class "Controller" in Gorm because it is not a GUI component. Without instantiation, I can't connect the "controller" to the "view".