自訂圖形元件

在 Gorm 中也可以加入自訂的圖形元件, 而不使用 Gorm 內建的. 在這裡做一個顯示時間的圖形元件. 下圖中白色框線內即為自訂的圖形元件.

圖形 12.12. TimeMachine with custom view

TimeMachine with custom view

在 Gorm 中使用自訂元件時, 要先設計好其類別. 圖形元件可以繼承自 NSView 或 NSControl. NSView 要是是顯示, NSControl 則可以接受使用者的動作. NSControl 也是繼承自 NSView. 在這範列中, 要製做的元件稱為 TimeView, 繼承自 NSControl. 先選 NSResponder 旁的小圓鈕, 展開其子類別 (Subclass), 再展開 NSView 的字類別. 許多圖形元件都是繼承自 NSControl, 如 NSTextField.

圖形 12.13. NSControl in Gorm

NSControl in Gorm

選 NSControl, 選 "Classes->Create Subclass..." 產生新類別. 點兩下修改名稱為 "TimeView".

圖形 12.14. Add subclass of NSControl

Add subclass of NSControl

TimeView 同時也繼承了 NSControl 的三個 outlets 及七個 actions.

接著建立以下的圖形介面.

圖形 12.15. Interface with custom view

Interface with custom view

選其中的 CustomView, 選 Inspector 的 Attributes, 選 TimeView 類別. 如此便指定 CustomView 使用 TimeView 類別.

圖形 12.16. Change class of custom view

Change class of custom view

這時 CustomView 變成了 TimeView.

圖形 12.17. Custom view with TimeView class

Custom view with TimeView class

如同之前建立一個 Controller, 加入 timeView outlet 及 -showCurrentTime: action.

圖形 12.18. Add outlet

Add outlet

圖形 12.19. Add action

Add action

產生 Controller 物件, 連結 timeView outlet 至 TimeView 這個自訂元件. 連結按鈕到 -showCurrentTime 這個 action.

圖形 12.20. Connect outlet

Connect outlet
Connect outlet
Connect outlet

最後, 產生 TimeView 及 Controller 的程式碼, 並將圖形介面存檔成 TimeMachine.gorm. 這就是在 Gorm 中使用自訂元件的方法.

接下來就是要製做這個自訂元件, TimeView. 這個 TimeView 實際上是由 NSBox 及 NSTextView 所組成的. NSBox 即為其外框, NSTextField 則用來顯示字串. 做成自訂元件的好除了以後可以反複使用. 繼承自 NSView 的類別, 其啟始呼叫是 -initWithFrame: 因此要在 -initWithFrame 中加入要顯示的元件.

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

有些 Gorm 產生的程式碼不一定會用到, 因此可以直接刪除. 在 TimeView.h, 宣告了四個 NSTextField 來顯示時間, 一個 NSCalendarDate 來記錄時間. 也增加兩個 methods 來更改時間. 這是典型的做法. 在 initWithFrame 中, 先設定好 NSBox, 再加入四個 NSTextField 到 NSBox. NSBox 便成為 NSTextField 的 Superview, 而 NSTextField 便成為 NSBox 的 Subview. Superview 會保留 (retain) subview, 所以在這裡可以將其釋放 (release). 最後再將 NSBox 加入 TimeView 中, 這時 NSBox 成為 TimeView 的 subview, 而 TimeView 成為 NSBox 的 superview.

其他部份就簡單許多了:

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

在 Gorm 中, 並沒有產生 TimeView 物件. 因為在圖形介面產生一個自訂元件, 並指定該自訂元件為 TimeView, 便等同產生一個 TimeView 物件. 而 Controller 因為不是圖形元件, 所以必需自行產生一個物件, 才能連結 outlet 及 action.