Drag rectangle in cocoa

I draw a rectangle in a custom subclass of NSView, which can then be dragged within the borders of the view:

enter image description here

Code for this:

// Get the starting location of the mouse down event. NSPoint location = [self convertPoint: [event locationInWindow] fromView: nil]; // Break out if this is not within the bounds of the rect. if (!NSPointInRect(location, [self boundsOfAllControlPoints])) { return; } while (YES) { // Begin modal mouse tracking, looking for mouse dragged and mouse up events NSEvent *trackingEvent = [[self window] nextEventMatchingMask:(NSLeftMouseDraggedMask | NSLeftMouseUpMask)]; // Get tracking location and convert it to point in the view. NSPoint trackingLocation = [self convertPoint:[trackingEvent locationInWindow] fromView:nil]; // Calculate the delta of x and y compared to the previous point. long dX = location.x - trackingLocation.x; long dY = location.y - trackingLocation.y; // Update all points in the rect for (int i = 0; i < 4; i++) { NSPoint newPoint = NSMakePoint(points[i].x - dX, points[i].y - dY); points[i] = newPoint; } NSLog(@"Tracking location x: %fy: %f", trackingLocation.x, trackingLocation.y); // Set current location as previous location. location = trackingLocation; // Ask for a redraw. [self setNeedsDisplay:YES]; // Stop mouse tracking if a mouse up is received. if ([trackingEvent type] == NSLeftMouseUp) { break; } } 

I basically catch the mouse event and check to see if its location is inside the draggable rectangle. If so, I start tracking mouse movement in trackingEvent. I compute the delta for the x and y coordinates, create new points for the draggable rectangle, and request an update to the view display.

Although it works, it looks a bit β€œamateurish,” as when dragging and dropping, the mouse pointer will catch up with the shape that it drags and eventually crosses its borders. In other drag and drop operations, we will see that the mouse pointer is fixed at the position of the dragged object: from the beginning to the end of the drag and drop operation.

What causes this effect?

EDIT:

I changed my approach after Rob's answer and applied three methods:

 - (void) mouseDown: (NSEvent*) event { // There was a mouse down event which might be in the thumbnail rect. [self setDragStartPoint: [self convertPoint: [event locationInWindow] fromView: nil]]; // Indicate we have a valid start of a drag. if (NSPointInRect([self dragStartPoint], [self boundsOfAllControlPoints])) { [self setValidDrag: YES]; } } - (void) mouseDragged: (NSEvent *) anEvent { // Return if a valid drag was not detected during a mouse down event. if (![self validDrag]) { return; } NSLog(@"Tracking a drag."); // Get tracking location and convert it to point in the view. NSPoint trackingLocation = [self convertPoint: [anEvent locationInWindow] fromView: nil]; // Calculate the delta of x and y compared to the previous point. long dX = [self dragStartPoint].x - trackingLocation.x; long dY = [self dragStartPoint].y - trackingLocation.y; // Update all points in the rect for (int i = 0; i < 4; i++) { NSPoint newPoint = NSMakePoint(points[i].x - dX, points[i].y - dY); points[i] = newPoint; } // Ask for a redraw. [self setNeedsDisplay:YES]; NSLog(@"Tracking location x: %fy: %f", trackingLocation.x, trackingLocation.y); // Set current location as previous location. [self setDragStartPoint: trackingLocation]; NSLog(@"Completed mouseDragged method. Allow for repaint."); } - (void) mouseUp: (NSEvent *) anEvent { // End the drag. [self setValidDrag: NO]; [self setNeedsDisplay: YES]; } 

Although the effect is slightly better, there is still a noticeable delay with direct removal in the direction of the mouse pointer. This is especially noticeable when I move the mouse slowly while dragging.

EDIT 2:

Got it. The problem was the calculation of deltas. I have been using this for a long time while I have to use float. It works great.

+6
source share
1 answer

You hold onto the event loop during the rally, which means the square is never redrawn. Your call to setNeedsDisplay: draws nothing. This is just a fake view that needs to be redrawn. It cannot be redrawn until this procedure returns, which you do not do until the mouse button is released.

Read Handling Drag and Drop Operations for a full discussion of how to implement drag and drop in Cocoa. You need to either return from mouseDown: or redefine mouseDragged: and mouseUp: or you need to manually pump the event loop so that the drawing process can be processed.

I tend to recommend the first approach, although it requires several methods. Pumping an event loop can create very surprising errors and should be used with caution. The most common mistakes in my experience are related to the delay in the operation of selectors when you pump up the event loop, causing the "extra" code to run in the middle of your drag and drop procedure. In some cases, this can cause re-entry and deadlock. (It happened to me ....)

+9
source

Source: https://habr.com/ru/post/903147/


All Articles