[Drawkit] tool to create object of pre-set size then click to place?

Allan Daly allandaly at me.com
Thu Dec 31 10:26:42 PST 2009


I figured something out -- here's my approach in case it's useful to  
anyone. I'd be glad to hear any other/better ideas on this.

I created a new "stamp tool" that is a subclass of  
DKObjectCreationTool. It sets the cursor with an image based on the  
prototype object so that when the tool is clicked the cursor changes  
to an image of the object that will be "stamped." On a mouse down  
event the prototype object is added to the target layer. If "sticky  
tools" are set ( [DKToolController  
setAutomaticallyRevertsToSelectionTool:NO]) then you can stamp/insert  
many of the same object over and over again.

I gave this subclass a "cursorImage" method so that it can draw itself  
for the cursor, but also so it can draw itself to set a tool palette  
icon. When initializing a tool palette then this code can set the  
image for that tool.

	if ([[DKDrawingTool drawingToolWithName:[cell title]]  
respondsToSelector:@selector(cursorImage)])
	{
		[cell setImage:[[DKDrawingTool drawingToolWithName:[cell title]]  
cursorImage]];
	}


The tool is initialized at some point in the application with code  
along the lines of the following.

	// register the "center mark" tool, which is not one of the DK  
standard tools
	DKDrawableShape * shape = [[DKDrawableShape alloc] init];
	[shape setPath:[DKShapeFactory centerMark]];
	DKDrawingTool * dt = [[BBStampTool alloc]  
initWithPrototypeObject:shape];
	[(BBStampTool *)dt setCursorDimension:48.0];
	[DKDrawingTool registerDrawingTool:dt  withName:@"Center Mark"];


Here's the implementation of the stamp tool.

// ---------- BBStampTool.h --------------

#import <Cocoa/Cocoa.h>
#import <GCDrawKit/DKDrawkit.h>

@interface BBStampTool : DKObjectCreationTool
{
	float _cursorDim;
}

@property float cursorDimension;

- (NSImage *) cursorImage;

@end

// ---------- BBStampTool.m --------------

#import "BBStampTool.h"

@implementation BBStampTool

@synthesize cursorDimension = _cursorDim;

#pragma mark -
#pragma mark - As a BBStampTool

- (NSImage *) cursorImage
{
	// create a size based on the ivar _cursorDim
	NSSize cursorSize = NSMakeSize(_cursorDim, _cursorDim);
	
	// create and configure shape in order to create the cursor image
	DKDrawableShape * s = [self objectFromPrototype];
	[s setSize: cursorSize];
	[s setStyle:[DKStyle defaultStyle]];
	
	// create and return the cursor image based on the tool's shape
	NSImage * i = [s swatchImageWithSize:cursorSize];
	
	return i;
}

#pragma mark -
#pragma mark - As part of DKDrawingTool Protocol

+ (BOOL) toolPerformsUndoableAction
{
	return YES;
}

- (NSString *) actionName
{
	return @"object stamp";
}

- (NSCursor*) cursor
{
	NSCursor * c = [[NSCursor alloc] initWithImage:[self cursorImage]  
hotSpot:NSMakePoint(_cursorDim/2, _cursorDim/2)];
	return c;
}

- (int)	mouseDownAtPoint:(NSPoint) p targetObject:(DKDrawableObject*)  
obj layer:(DKLayer*) layer event:(NSEvent*) event delegate:(id) aDel
{
	// condition point p based on drawing snap settings
	BOOL controlKey = ([event modifierFlags] & NSControlKeyMask) != 0;
	p = [[layer drawing] snapToGrid:p withControlFlag:controlKey];
	
	// create a size based on the ivar _cursorDim
	NSSize objectSize = NSMakeSize(_cursorDim, _cursorDim);

	// create and configure the tool's object
	DKDrawableObject * objToInsert = [self objectFromPrototype];
	[objToInsert setSize:objectSize];
	[objToInsert setLocation:p];
	
	// turn off recording of undo until we commit the object
	[[layer undoManager] disableUndoRegistration];
	
	// add the pending object
	[(DKObjectOwnerLayer*)layer addObjectPendingCreation:objToInsert];
	
	// if the object created is not valid, the pending add to the layer  
needs to be
	// aborted. Otherwise the object is committed to the layer
	
	if (![objToInsert objectIsValid])
	{
		[(DKObjectOwnerLayer*)layer removePendingObject];
		
		// turn undo back on
		[[layer undoManager] enableUndoRegistration];
	}
	else
	{
		// a valid object was made, so commit it to the layer and select it
		// turn undo back on and commit the object
		[[layer undoManager] enableUndoRegistration];
		[aDel toolWillPerformUndoableAction:self];
		
		[(DKObjectDrawingLayer*)layer commitPendingObjectWithUndoActionName: 
[self actionName]];
		[(DKObjectDrawingLayer*)layer replaceSelectionWithObject:objToInsert];
	}
	
	// return value is the partcode of the target that was hit
	// this tool does not operate on a target so return 0 (no object)
	return 0;
}

- (void) mouseDraggedToPoint:(NSPoint) p partCode:(int) pc layer: 
(DKLayer*) layer event:(NSEvent*) event delegate:(id) aDel
{
	// dragging not supported by the stamp tool, so this method does  
nothing
}

- (BOOL) mouseUpAtPoint:(NSPoint) p partCode:(int) pc layer:(DKLayer*)  
layer event:(NSEvent*) event delegate:(id) aDel
{
	// return value tells app if the tool did something undoable
	return YES;
}

#pragma mark -
#pragma mark - As a DKObjectCreationTool

- (id) initWithPrototypeObject:(id <NSObject>) aPrototype
{
	self = [super initWithPrototypeObject:aPrototype];
	if (self != nil )
	{
		// set a default value for _cursorDim
		[self setCursorDimension:50.0];
	}
	return self;
}

@end


Finally, for kicks and completeness here's the category on  
DKShapeFactory that makes this "center mark" shape.

#import "DKShapeFactory+NewShapes.h"

@implementation DKShapeFactory (NewShapes)

+ (NSBezierPath *) centerMark
{
	float radius = 0.25;
	NSBezierPath * path = [NSBezierPath bezierPath];
	[path setWindingRule: NSEvenOddWindingRule];
	
	NSPoint pMiddle  = NSMakePoint(	   0.0,   0.0);
	NSPoint pRight   = NSMakePoint( radius,   0.0);
	NSPoint pLeft    = NSMakePoint(-radius,   0.0);
	
	// add the cross
	[path moveToPoint: NSMakePoint(    0, -0.5 )];
	[path lineToPoint: NSMakePoint(    0,  0.5 )];
	[path moveToPoint: NSMakePoint( -0.5,    0 )];
	[path lineToPoint: NSMakePoint(  0.5,    0 )];
	
	// add the circle
	[path appendBezierPathWithArcWithCenter:pMiddle radius:radius  
startAngle:0 endAngle:360 clockwise:NO];
	
	// top right quadrant "hole"
	[path moveToPoint: pMiddle];
	[path lineToPoint: pRight];
	[path appendBezierPathWithArcWithCenter: pMiddle radius: radius  
startAngle: 0.0 endAngle: 90.0 ];
	[path lineToPoint: pMiddle];
	
	// bottom left quadrant "hole"
	[path lineToPoint: pLeft];
	[path appendBezierPathWithArcWithCenter: pMiddle radius: radius  
startAngle: 180.0 endAngle: 270.0];
	[path closePath];
	return path;
}

@end

Have fun with it, and happy 2010 everyone.

-Allan

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.apptree.net/pipermail/drawkit-apptree.net/attachments/20091231/bc53091a/attachment-0001.htm>


More information about the Drawkit mailing list