(前回)Cocoaの日々: Application List (3) アイコン表示
NSTableView 内でドラッグ&ドロップを使った並び替えの機能を実装する。
再びリファレンスを見て必要な実装を入れる。
Table View Programming Guide: Using Drag and Drop in Tables
1. registerForDraggedTypes: でサポートするデータタイプを定義
2. tableView:writeRowsWithIndexes:toPasteboard: でドラッグ開始
3. tableView:validateDrop:proposedRow:propsedDropOperation: でドロップ受け入れ準備
4. tableView:acceptDrop:row:dropOperation: でドロップ処理
まずデータタイプを定義。
#define AppListTableViewDataType @"AppListTableViewDataType"
:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[tableView_ registerForDraggedTypes:
[NSArray arrayWithObjects:
NSFilenamesPboardType, AppListTableViewDataType, nil]];
:
}
次にドラッグ開始時の処理。ドラッグ対象の行データを NSKeyedArchiver を使い NSData に変換し、ペーストボードへ登録する。
- (BOOL)tableView:(NSTableView *)tv writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard*)pboard
{
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:rowIndexes];
[pboard declareTypes:[NSArray arrayWithObject:AppListTableViewDataType] owner:self];
[pboard setData:data forType:AppListTableViewDataType];
return YES;
}
ドロップ受け入れ判定。NSTableView 内の場合、NSDragOperationMove (移動) とする。
- (NSDragOperation)tableView:(NSTableView *)aTableView validateDrop:(id < NSDraggingInfo >)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)operation
{
[aTableView setDropRow:row dropOperation:NSTableViewDropAbove];
if ([info draggingSource] == tableView_) {
return NSDragOperationMove;
}
return NSDragOperationEvery;
}
最後はドロップ時の処理だが、とりあえずここまでで動かしてみる。
ん?ドラッグしても選択されるだけで、行をドラッグできない。なぜだろう。
いろいろ調べていると NSTableView はドラッグ時に -[NSCell hitTestForEvent:inRect:ofView:] を呼び出し、この結果でドラッグするかを判断しているらしい (v10.5から)。
情報源:
reordering table cells with 10.5 sdk | Cocoabuilder
上記に掲載されていた情報を引用すると:
NSTableView/NSOutlineView - Cell Hit Testing, Drag and Drop, and※上記は developer.apple.com の releaesnotes に掲載されていたらしいが、みつからなかった。
Cell EditingNSTableView now uses the new NSCell hit testing API to
perform certain actions. Custom NSCell subclasses in applications
that link on or after Leopard should properly implement -
hitTestForEvent:inRect:ofView:; see NSCell.h for more information.
NSTableView performs hit testing in the cells to do the following
actions:
- Drag and Drop: NSTableView calls hitTestForEvent:inRect:ofView in
canDragRowsWithIndexes:atPoint. If the hit cell returns
NSCellHitTrackableArea, the particular row will be tracked instead
of dragged.
- Cell Editing: When NSTableView recieves a mouse down, single-click
editing of text (like Finder) will happen if there is only one row
selected, and the cell returns NSCellHitEditableTextArea.
See the DragNDropOutlineView demo application for an example of how
to properly implement the NSCell methods.
関連情報:
NSCell Class Reference - Hit Testing
NSCell Class Reference - hitTestForEvent:inRect:ofView:
DragNDropOutlineView
そこで NSCell のサブクラスで hitTestForEvent:inRect:ofView: を実装してみた。といっても単純に NSCellHitContentArea を返すだけ。
ApplicationCell.m
- (NSUInteger)hitTestForEvent:(NSEvent *)event inRect:(NSRect)cellFrame ofView:(NSView *)controlView {
return NSCellHitContentArea;
}
これでどうだろう。
ドラッグできた。挿入位置に横線が入るようになった。ただ描画がおかしい。何故かドラッグしている行が一番上の行に描画されている。うーむ。