テキストボックスの実装に取りかかる。cocoaには NSTextFieldが用意されているのでこれを使う。SimpleViewerは自前でインスタンスを用意しているので、InterfaceBuilderは使えない。同じく自前でインスタンス化する必要がある。少しカスタマイズをするので派生クラスを用意することにした。
FilenameTextField.h
@interface FilenameTextField : NSTextField {
SimpleViewerController* _controller;
}
- (id)initWithController:(SimpleViewerController*)controller;
- (void)setDisable;
- (void)startEdit;
@end
SimpleViewerとのやりとりがあるのでコントローラのインスタンスを持っている。メソッドにはエディットの開始を指示する #startEdit と終了時の処理を行う #setDisable を用意する。
初期化はこんな感じ。InterfaceBuilderだとインスペクタを使って設定するものを全部自前でセットしている。
FilenameTextField.m
- (id)initWithController:(SimpleViewerController*)controller
{
self = [super initWithFrame:NSZeroRect];
if (self) {
_controller = [controller retain];
[self setDelegate:_controller];
[self setBordered:NO];
[self setEditable:NO];
[self setEnabled:NO];
[self setSelectable:NO];
[self setDrawsBackground:NO];
[self setTextColor:[NSColor whiteColor]];
[self setBackgroundColor:[NSColor colorWithDeviceWhite:0.2 alpha:0.9]];
[self setBezeled:NO];
[self setFocusRingType:NSFocusRingTypeNone];
[[self cell] setLineBreakMode:NSLineBreakByTruncatingTail];
[[self cell] setAlignment:NSCenterTextAlignment];
[self setFont:[NSFont titleBarFontOfSize:12.0]];
}
return self;
}
初期表示では編集ができない表示のみの状態、いわゆるラベルとして働く。
enterキーが押されたり、ファイル名がダブルクリックされたら #startEdit を呼出す。enterキーはコントローラの方でハンドリングしているので、ここではダブルクリックの処理を示しておく。FilenameTextFieldクラスは NSTextFieldの派生クラスなのでマウスイベントを扱える。そこで #mouseDown:をオーバーライドしてダブルクリックを補足する。
- (void)mouseDown:(NSEvent*)theEvent
{
if ([theEvent clickCount] >= 2) {
[self startEdit];
// [super mouseDown:theEvent]; // do not
}
}
ダブルクリックされたら #startEdit を呼出す。#startEdit では表示のみのラベル状態から、編集可能な状態へ設定を変更する。
- (void)startEdit
{
NSString* filename = [_controller filename];
if (!filename || [filename isEqualToString:@""]) {
return;
}
filename = [filename lastPathComponent];
[self setDrawsBackground:YES];
[self setEditable:YES];
[self setEnabled:YES];
[self setEditable:YES];
[[self window] makeFirstResponder:self];
NSString* name = [filename stringByDeletingPathExtension];
[self setStringValue:name];
}
ファイル名は拡張子を除いた部分だけを変更可能にする。
そして return キーが押されたら変更を確定する。returnキーが押されたり、他の要因によって編集が完了した場合は#textDidEndEditing: が呼出される。
- (void)textDidEndEditing:(NSNotification *)notification
{
[_controller endEditFilenameIsCancel:NO];
}
ここでは一旦コントローラにお伺いを立てにいく。コントローラでは名前のチェック等を行い、その後ファイル名の変更を実行する。その後、#setDisable が呼出される。
- (void)setDisable
{
if ([self isEditable]) {
[self setDrawsBackground:NO];
[self setTextColor:[NSColor whiteColor]];
[self setEditable:NO];
[self setEnabled:NO];
[[self window] makeFirstResponder:nil];
[self setNeedsDisplay:YES]; // important!!
}
}
#startEdit: と #setDisable が対となって、ラベルと編集状態を行き来している。
ラベル(表示のみ)
|
|#startEdit:
↓
編集状態
|
|#setDisable
↓
ラベル
ポイントは #setEnable: を使っているところ。普通は #setEditable: だけで良さそうだがダメだった。試行錯誤したところ @setEnable: を組み合わせると上記のような意図した動作になることが分かったので採用している。また NSWindow#makeFirstResponder: を呼出してフォーカスを当てたり、外したりの制御を行っている。
さて動かしてみよう。こんな感じ。
returnキーを押すと編集状態からラベルへ復帰する。