それでは最後に、これまでの応用として複数のセルを組み合わせて一つのセルを作ってみましょう。
セルの上に複数のセルを乗せる訳ですが、これは基本的にビューにセルを乗せるのと同じような方法で実現できます。
まず、内包するセルを生成して、インスタンス変数に格納します。
@interface MyCellOnCell : NSActionCell
{
MyCell* trackPadCell;
NSSliderCell* xSlider;
NSSliderCell* ySlider;
}
@end
- (id)init
{
if(!(self = [super init])) return nil;
trackPadCell = [[MyCell alloc] init];
[trackPadCell setTarget:self];
[trackPadCell setAction:@selector(trackAction:)];
xSlider = [[NSSliderCell alloc] init];
[xSlider setTarget:self];
[xSlider setAction:@selector(sliderAction:)];
ySlider = [[NSSliderCell alloc] init];
[ySlider setTarget:self];
[ySlider setAction:@selector(sliderAction:)];
return self;
}
セルの値やプロパティーに対するアクセスは、内包するセルに中継しなければなりません。
- (NSRect)area
{
return trackPadCell.area;
}
- (void)setArea:(NSRect)aRect
{
trackPadCell.area = aRect;
[xSlider setMaxValue:NSMaxX(aRect)];
[xSlider setMinValue:NSMinX(aRect)];
[ySlider setMaxValue:NSMaxY(aRect)];
[ySlider setMinValue:NSMinY(aRect)];
}
- (id)objectValue
{
return [trackPadCell objectValue];
}
- (void)setObjectValue:(id)value
{
[trackPadCell setObjectValue:value];
if([value respondsToSelector:@selector(pointValue)])
{
NSPoint point = [value pointValue];
[xSlider setFloatValue:point.x];
[ySlider setFloatValue:point.y];
}
}
- (BOOL)isContinuous
{
return [trackPadCell isContinuous];
}
- (void)setContinuous:(BOOL)flag
{
[trackPadCell setContinuous:flag];
}
セルの値は、-objectValueと-setObjectValue:をオーバーライドして、中継するコードを書きます。
Continuousプロパティーは、セル内をドラッグしたときに継続的にアクションを送信するかどうかを示すフラグです。
ビューにセルを乗せたときと同じように、セル内の各領域を定義するメソッドを書きます。
- (NSRect)padRectFromCellFrame:(NSRect)cellFrame
{
NSRect rect;
rect.origin = NSMakePoint(0.0, 20.0);
rect.size.width = cellFrame.size.width - 20.0;
rect.size.height = cellFrame.size.height - 20.0;
return rect;
}
- (NSRect)xSliderRectFromCellFrame:(NSRect)cellFrame
{
return NSMakeRect(0.0, 0.0, cellFrame.size.width - 20.0, 20.0);
}
- (NSRect)ySliderRectFromCellFrame:(NSRect)cellFrame
{
NSRect rect;
rect.origin = NSMakePoint(cellFrame.size.width - 20.0, 20.0);
rect.size = NSMakeSize(20.0, cellFrame.size.height - 20.0);
return rect;
}
セルはビューと違って、自身の矩形を取得するメソッドがないので、メソッドのパラメータとしてそれを受け取るようにしてます。
内包するセルを描画するには、単純に-drawWithFrame:inView:を中継させるだけです。
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
[self setControlView:controlView];
NSRect padRect = [self padRectFromCellFrame:cellFrame];
[trackPadCell drawWithFrame:padRect inView:controlView];
NSRect xSliderRect = [self xSliderRectFromCellFrame:cellFrame];
[xSlider drawWithFrame:xSliderRect inView:controlView];
NSRect ySliderRect = [self ySliderRectFromCellFrame:cellFrame];
[ySlider drawWithFrame:ySliderRect inView:controlView];
}
マウスによるセルの操作も基本的に-trackMouse:inRect:ofView:untilMouseUp:を内包するセルへ中継するコードになります。その際には、マウスの位置からどのセルに中継するかを選択します。
- (BOOL)trackMouse:(NSEvent *)theEvent
inRect:(NSRect)cellFrame
ofView:(NSView *)controlView
untilMouseUp:(BOOL)untilMouseUp
{
NSPoint location = [theEvent locationInWindow];
NSPoint point = [controlView convertPoint:location fromView:nil];
if(NSPointInRect(point, [self padRectFromCellFrame:cellFrame]))
{
return [trackPadCell trackMouse:theEvent
inRect:[self padRectFromCellFrame:cellFrame]
ofView:controlView
untilMouseUp:untilMouseUp];
}
if(NSPointInRect(point, [self xSliderRectFromCellFrame:cellFrame]))
{
return [xSlider trackMouse:theEvent
inRect:[self xSliderRectFromCellFrame:cellFrame]
ofView:controlView
untilMouseUp:untilMouseUp];
}
if(NSPointInRect(point, [self ySliderRectFromCellFrame:cellFrame]))
{
return [ySlider trackMouse:theEvent
inRect:[self ySliderRectFromCellFrame:cellFrame]
ofView:controlView
untilMouseUp:untilMouseUp];
}
return NO;
}
セルの操作によってセルの値が変化すると、セルからアクションが送信されるので、それを受信して内包される各セルの値を同期させると共に、アクションを再送信します。このとき、アクションの送信元はこのセルではなく、セルが置かれているコントロールにします。
- (void)trackAction:(id)sender
{
NSPoint point = [[trackPadCell objectValue] pointValue];
[xSlider setFloatValue:point.x];
[ySlider setFloatValue:point.y];
if([self action] != nil)
{
[NSApp sendAction:[self action] to:[self target] from:[self controlView]];
}
}
- (void)sliderAction:(id)sender
{
NSPoint point = NSMakePoint([xSlider floatValue], [ySlider floatValue]);
[trackPadCell setObjectValue:[NSValue valueWithPoint:point]];
if([self action] != nil)
{
[NSApp sendAction:[self action] to:[self target] from:[self controlView]];
}
}