1

I'm creating a webapp in Cappuccino, and I need a way to draw shapes (rectangles, images etc) onto a CPWindow. Is there any widget/control I can use to do this? Are there any controls like this in other frameworks like Sproutcore? Or do I have to implement my own?

I'd also like to know if there's a way of making shapes draggable like this?

1 Answers1

1

In Cappuccino you can perform custom drawing in any view. To do this, override the drawRect: method (of anything that inherits from CPView, which is pretty much every control). You can either use the CPGraphics tools like CPBezierPath, or you can draw using CoreGraphics with commands like CGContextAddPath - to learn more about this latter style of drawing the Apple docs on Core Graphics for Mac OS X are helpful. Remember that Cappuccino is based on Objective-C and Cocoa.

Here's an example view that draws a yellow star with a dotted border in a rounded rectangle, sized to fit the current view:

@implementation CustomDrawView : CPView
{
}

- (void)drawRect:(CGRect)aRect
{
    [super drawRect:aRect];

    [[CPColor whiteColor] set];
    [[CPBezierPath bezierPathWithRect:bounds] fill];

    var frame =[self bounds],
        shadow = [[CPShadow alloc] init];

    [shadow setShadowColor:[CPColor blackColor]];
    [shadow setShadowOffset:CGSizeMake(0, 3)];
    [shadow setShadowBlurRadius:5];

    //// Rounded Rectangle Drawing
    var roundedRectanglePath = [CPBezierPath bezierPathWithRoundedRect:CGRectMake(CGRectGetMinX(frame) + 3.5, CGRectGetMinY(frame) + 3.5, CGRectGetWidth(frame) - 7, CGRectGetHeight(frame) - 7) xRadius:7 yRadius:7];
    [[CPColor blackColor] setStroke];
    [roundedRectanglePath setLineWidth:1];
    var roundedRectanglePattern = [5, 1, 1, 1];
    [roundedRectanglePath setLineDash:roundedRectanglePattern phase:0];
    [roundedRectanglePath stroke];

    var starPath = [CPBezierPath bezierPath];
    [starPath moveToPoint:CGPointMake(CGRectGetMinX(frame) + 0.50000 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.20513 * CGRectGetHeight(frame))];
    [starPath lineToPoint:CGPointMake(CGRectGetMinX(frame) + 0.43029 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.35357 * CGRectGetHeight(frame))];
    [starPath lineToPoint:CGPointMake(CGRectGetMinX(frame) + 0.31200 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.40445 * CGRectGetHeight(frame))];
    [starPath lineToPoint:CGPointMake(CGRectGetMinX(frame) + 0.38720 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.54707 * CGRectGetHeight(frame))];
    [starPath lineToPoint:CGPointMake(CGRectGetMinX(frame) + 0.38381 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.72696 * CGRectGetHeight(frame))];
    [starPath lineToPoint:CGPointMake(CGRectGetMinX(frame) + 0.50000 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.66667 * CGRectGetHeight(frame))];
    [starPath lineToPoint:CGPointMake(CGRectGetMinX(frame) + 0.61619 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.72696 * CGRectGetHeight(frame))];
    [starPath lineToPoint:CGPointMake(CGRectGetMinX(frame) + 0.61280 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.54707 * CGRectGetHeight(frame))];
    [starPath lineToPoint:CGPointMake(CGRectGetMinX(frame) + 0.68800 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.40445 * CGRectGetHeight(frame))];
    [starPath lineToPoint:CGPointMake(CGRectGetMinX(frame) + 0.56971 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.35357 * CGRectGetHeight(frame))];
    [starPath closePath];
    [[CPColor yellowColor] setFill];
    [starPath fill];
    [CPGraphicsContext saveGraphicsState];
    [shadow set];
    [[CPColor whiteColor] setStroke];
    [starPath setLineWidth:3];
    var starPattern = [5, 1, 5, 1];
    [starPath setLineDash:starPattern phase:2];
    [starPath stroke];
    [CPGraphicsContext restoreGraphicsState];
}

@end

Image of what the above code renders.

I extracted this from a larger set of drawing tests in Cappuccino.

Under the hood, Cappuccino will either use a canvas, when available, or VML when necessary (for some versions of IE).

Alexander Ljungberg
  • 6,302
  • 4
  • 31
  • 37
  • This is very good, but is there any way to move shapes around with the mouse [like this](http://rectangleworld.com/demos/OOPDragging/OOPDragging.html)? (sorry for not outlining this in the original question) – TheUberEpic Aug 13 '13 at 14:29
  • Yes, but how you do it depends a little on how many shapes you want. If you only have a few shapes I would recommend making each shape its own CPView. This makes it easy to react to clicks and drags. On the other hand if you're going to have quite a few, you should use a single CPView which draws all the rectangles and interprets mouse interactions by itself through `mouseDown:`, `mouseUp:` and `mouseDragged:`. Here's an example for how to detect when the mouse cursor is inside of a shape (a path): https://github.com/cappuccino/cappuccino/blob/master/Tests/Manual/CGPath/AppController.j#L68. – Alexander Ljungberg Aug 13 '13 at 15:17