トップページドキュメント > コントロールの作成

セルにセルを乗せる

それでは最後に、これまでの応用として複数のセルを組み合わせて一つのセルを作ってみましょう。

セルの上に複数のセルを乗せる訳ですが、これは基本的にビューにセルを乗せるのと同じような方法で実現できます。


内包するセルを生成する

まず、内包するセルを生成して、インスタンス変数に格納します。

@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]];
    }
}
Copyright(C)2001-2010 STRIPE-NET. All Right Reserved.