Re: A cursor bug in DragItemAround example
Re: A cursor bug in DragItemAround example
- Subject: Re: A cursor bug in DragItemAround example
- From: an0 <email@hidden>
- Date: Sun, 27 Apr 2008 17:35:52 +0800
On Sun, Apr 27, 2008 at 3:34 PM, Quincey Morris
<email@hidden> wrote:
>
> On Apr 26, 2008, at 23:14, an0 wrote:
> Setting a cursor always "works", but is useless if something else changes
> it immediately afterwards. I think that might be what's happening here.
> (Also, if view comes from a NIB, initWithCoder may be called instead of
> initWithFrame.)
"What's happening here" is exactly what I want to know. I think
awakeFromNib should set the scene no matter initWithCoder or
initWithFrame is called, so I do my initialization in awakeFromNib.
> I think you have 2 separate cursor management problems to solve:
>
> 1. When not dragging. Tracking areas may be useful, even if the items are
> not subviews. You should have a cursorUpdate method, otherwise some default
> implementation of it may keep changing the cursor on you. The cursorUpdate
> event will tell you which NSTrackingArea is involved, so you can use that to
> work out how to choose the cursor.
>
> 2. When dragging. Tracking areas don't matter, because (presumably) you
> just want to force a single cursor wherever the mouse goes. Your
> cursorUpdate method needs to take the dragging state into account. As I said
> before, you may not be able to control the cursor completely, when it is
> *outside* your view during a drag.
What you said is exactly what I've done.
> If you change the NSTrackingArea at mouseUp, you should probably specify
> the NSTrackingAssumeInside option too, since (presumably) you are placing
> the tracking area so that the mouse is actually inside. (The documentation
> for this option is almost completely wrong. See the Leopard developer
> release notes if you want the correct description.)
I've appended NSTrackingAssumeInside option as you advise, but the
misbehavior remains.
At this point, I think it is more convenient if I make my code
available, so that you can know better what I am trying to achieve and
find what I've done inappropriately and revise it accordingly. Thank
you for your time in advance. The code is simple and short.
DraggableItemView.h:
#import <Cocoa/Cocoa.h>
@interface DraggableItemView : NSView {
NSPoint location;
NSTrackingArea *trackingRect;
// private variables that track state
BOOL dragging;
BOOL inTrackingRect;
NSPoint lastDragLocation;
}
@property(retain) NSTrackingArea *trackingRect;
// -----------------------------------
// Modify the Rectange location
// -----------------------------------
- (void)offsetLocationByX:(float)x andY:(float)y;
-(IBAction)setItemPropertiesToDefault:(id)sender;
// -----------------------------------
// Various Accessor Methods
// -----------------------------------
- (void)setLocation:(NSPoint)point;
- (NSPoint)location;
- (NSRect)calculatedItemBounds;
- (BOOL)isPointInItem:(NSPoint)testPoint;
@end
DraggableItemView.m:
#import "DraggableItemView.h"
@implementation DraggableItemView
@synthesize trackingRect;
- (void)awakeFromNib
{
[self setItemPropertiesToDefault:self];
}
- (void)dealloc
{
[trackingRect release];
[super dealloc];
}
// -----------------------------------
// Draw the View Content
// -----------------------------------
- (void)drawRect:(NSRect)rect
{
// erase the background by drawing white
[[NSColor whiteColor] setFill];
[NSBezierPath fillRect:rect];
// set the current color for the draggable item
[[NSColor redColor] setFill];
// draw the draggable item
[NSBezierPath fillRect:[self calculatedItemBounds]];
}
- (BOOL)isOpaque
{
return YES;
}
// -----------------------------------
// Modify the item location
// -----------------------------------
- (void)offsetLocationByX:(float)x andY:(float)y
{
// tell the display to redraw the old rect
[self setNeedsDisplayInRect:[self calculatedItemBounds]];
// since the offset can be generated by both mouse moves
// and moveUp:, moveDown:, etc.. actions, we'll invert
// the deltaY amount based on if the view is flipped or
// not.
int invertDeltaY = [self isFlipped] ? -1: 1;
location.x=location.x+x;
location.y=location.y+y*invertDeltaY;
// invalidate the new rect location so that it'll
// be redrawn
[self setNeedsDisplayInRect:[self calculatedItemBounds]];
}
// -----------------------------------
// Hit test the item
// -----------------------------------
- (BOOL)isPointInItem:(NSPoint)testPoint
{
BOOL itemHit=NO;
// test first if we're in the rough bounds
itemHit = NSPointInRect(testPoint,[self calculatedItemBounds]);
// yes, lets further refine the testing
if (itemHit) {
}
return itemHit;
}
// -----------------------------------
// Handle Mouse Events
// -----------------------------------
-(void)mouseDown:(NSEvent *)event
{
NSPoint clickLocation;
BOOL itemHit=NO;
// convert the click location into the view coords
clickLocation = [self convertPoint:[event locationInWindow]
fromView:nil];
// did the click occur in the item?
itemHit = [self isPointInItem:clickLocation];
// Yes it did, note that we're starting to drag
if (itemHit) {
// flag the instance variable that indicates
// a drag was actually started
dragging=YES;
// store the starting click location;
lastDragLocation=clickLocation;
// set the cursor to the closed hand cursor
// for the duration of the drag
[[NSCursor closedHandCursor] set];
}
}
-(void)mouseDragged:(NSEvent *)event
{
if (dragging) {
NSPoint newDragLocation=[self convertPoint:[event locationInWindow]
fromView:nil];
// offset the pill by the change in mouse movement
// in the event
[self offsetLocationByX:(newDragLocation.x-lastDragLocation.x)
andY:(newDragLocation.y-lastDragLocation.y)];
// save the new drag location for the next drag event
lastDragLocation=newDragLocation;
}
}
-(void)mouseUp:(NSEvent *)event
{
dragging=NO;
[self removeTrackingArea:trackingRect];
self.trackingRect = [[[NSTrackingArea alloc] initWithRect:[self
calculatedItemBounds]
options:NSTrackingCursorUpdate | NSTrackingActiveInActiveApp |
NSTrackingAssumeInside
owner:self
userInfo:nil] autorelease];
[self addTrackingArea:trackingRect];
[self displayIfNeeded];
[[NSCursor openHandCursor] set];
}
// -----------------------------------
// First Responder Methods
// -----------------------------------
- (BOOL)acceptsFirstResponder
{
return YES;
}
- (IBAction)setItemPropertiesToDefault:(id)sender
{
[self setLocation:NSMakePoint(0.0,0.0)];
NSTrackingAreaOptions options = NSTrackingCursorUpdate |
NSTrackingActiveInActiveApp;
NSPoint localPoint = [self convertPoint:[[self window]
mouseLocationOutsideOfEventStream]
fromView:nil];
if ([self isPointInItem:localPoint]) {
inTrackingRect = YES;
options |= NSTrackingAssumeInside;
[[NSCursor openHandCursor] set];
} else {
inTrackingRect = NO;
}
if (trackingRect) {
[self removeTrackingArea:trackingRect];
}
self.trackingRect = [[[NSTrackingArea alloc] initWithRect:[self
calculatedItemBounds]
options:options
owner:self
userInfo:nil] autorelease];
[self addTrackingArea:trackingRect];
}
-(void)cursorUpdate:(NSEvent *)event
{
if (dragging) {
return;
}
if (!inTrackingRect) {
[[NSCursor openHandCursor] set];
} else {
[[NSCursor arrowCursor] set];
}
inTrackingRect = !inTrackingRect;
}
- (void)setLocation:(NSPoint)point
{
// test to see if the point actually changed
if (!NSEqualPoints(point,location)) {
// tell the display to redraw the old rect
[self setNeedsDisplayInRect:[self calculatedItemBounds]];
// reassign the rect
location=point;
// display the new rect
[self setNeedsDisplayInRect:[self calculatedItemBounds]];
}
}
- (NSPoint)location {
return location;
}
- (NSRect)calculatedItemBounds
{
NSRect calculatedRect;
// calculate the bounds of the draggable item
// relative to the location
calculatedRect.origin=location;
calculatedRect.size.width=60.0;
calculatedRect.size.height=20.0;
return calculatedRect;
}
@end
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden