re: allow user to drag controls to make their own interface (EXAMPLE)
re: allow user to drag controls to make their own interface (EXAMPLE)
- Subject: re: allow user to drag controls to make their own interface (EXAMPLE)
- From: Mark Onyschuk <email@hidden>
- Date: Thu, 15 Apr 2004 18:08:57 -0400
Here is a quick and dirty example I whipped up of a palette view
containing subviews which can be dragged around. A sample project
containing this code (and some draggy color wells, sliders, etc :-) can
be found at:
http://www.voxsoftware.com/Examples/DraggableViews.zip
Here's the concept:
** Take a view and populate it with subviews in IB.
** On awakeFromNib, this view creates an image of each of its subviews
then removes them from the view hierarchy, while keeping a private
array of these subviews in the background.
** On mouseDown:, the view consults its private array of views and
their frames, and performs a drag if a hit is detected. My example
could be extended to initiate a drag-and-drop operation where the view
to be dragged is archived with NSArchiver and put on the dragging
pasteboard.
** On drawRect:, the view runs through its list of view images and
draws them at each view's frame position.
Note that the code I use to image widgets only works for non-compound
widgets (ie, widgets which themselves have no subviews). I believe that
OmniAppKit has a method which can image a view and all of its subviews
into an image...
The code follows:
@implementation PaletteView
- (void)dealloc
{
[subviewObjects release];
[subviewImages release];
[super dealloc];
}
- (void)awakeFromNib
{
int i, count;
subviewObjects = [[self subviews] mutableCopyWithZone:[self zone]];
subviewImages = [[NSMutableArray allocWithZone:[self zone]] init];
// take a snapshot of each
for (i = 0, count = [subviewObjects count]; i < count; i++) {
NSView *view = [subviewObjects objectAtIndex:i];
NSImage *image = [[[NSImage allocWithZone:[self zone]]
initWithSize:[view bounds].size] autorelease];
[image setFlipped:[view isFlipped]];
[image lockFocus];
[view drawRect:[view bounds]];
[image unlockFocus];
[subviewImages addObject:image];
}
// remove all subviews (we've retained them above)
[subviewObjects
makeObjectsPerformSelector:@selector(removeFromSuperview)];
}
- (NSView *)viewAtPoint:(NSPoint)aPoint
{
int i, count;
for (i = 0, count = [subviewObjects count]; i < count; i++) {
NSView *view = [subviewObjects objectAtIndex:i];
if (NSPointInRect(aPoint, [view frame])) {
return view;
}
}
return nil;
}
- (void)mouseDown:(NSEvent *)theEvent
{
NSView *view = [self viewAtPoint:[self convertPoint:[theEvent
locationInWindow] fromView:nil]];
if (view) {
[self dragImageOfView:view withEvent:theEvent];
}
}
- (void)dragImageOfView:(NSView *)aView withEvent:(NSEvent *)theEvent
{
NSPoint location = [self convertPoint:[theEvent locationInWindow]
fromView:nil];
while (1) {
NSEvent *nextEvent = [[self window]
nextEventMatchingMask:NSLeftMouseUpMask|NSLeftMouseDraggedMask];
NSPoint nextLocation = [self convertPoint:[nextEvent
locationInWindow] fromView:nil];
if ([nextEvent type] == NSLeftMouseUp) {
break;
}
float dx = nextLocation.x - location.x;
float dy = nextLocation.y - location.y;
NSRect viewFrame = [aView frame];
viewFrame.origin.x += dx;
viewFrame.origin.y += dy;
[aView setFrame:viewFrame];
[self display];
location = nextLocation;
}
}
- (void)drawRect:(NSRect)aRect
{
int i, count;
for (i = 0, count = [subviewObjects count]; i < count; i++) {
NSView *view = [subviewObjects objectAtIndex:i];
NSImage *image = [subviewImages objectAtIndex:i];
[image drawInRect:[view frame]
fromRect:NSZeroRect
operation:NSCompositeSourceOver
fraction:1];
}
}
@end
Resizing of views is left as an exercise for the reader :-) but it
involves re-caching the view image as it is resized (and of course the
actual logic of resizing a rect based on user gestures).
The complimentary view - the drag destination - can be coded in a
similar way. When users want to go into "run" mode, then images are
replaced with their actual object equivalents. In design mode, they're
switched back to their images. Using images instead of the real things
inside your canvas allows you do do things like draw "knobs" around
selected widgets without the hassle of view layering obscuring what
you're drawing...
-Mark
_______________________________________________
cocoa-dev mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/cocoa-dev
Do not post admin requests to the list. They will be ignored.