2011-12-05 11 views
6

Estoy dibujando un rectángulo en una subclase personalizada de NSView que puede ser arrastrado dentro de las fronteras de la vista:arrastrando un rectángulo en el cacao

enter image description here

El código para hacer esto es:

// 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's 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: %f y: %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; 
    } 

} 

Básicamente atrapo un evento de mouse y compruebo si su ubicación está dentro del rectángulo que se puede arrastrar. Si es así, empiezo a rastrear el movimiento del mouse en trackingEvent. Calculo los delta para las coordenadas xey, creo nuevos puntos para el rect ajustable y solicito una actualización de la visualización de las vistas.

Aunque funciona, se ve un poco "de aficionado" ya que durante el arrastre, el puntero del mouse se pondrá al día con la forma que se está arrastrando y eventualmente cruzará sus bordes. En otras operaciones de arrastre, se verá el puntero del mouse fijado a la posición del objeto que se está arrastrando, desde el inicio hasta el final de la operación de arrastre.

¿Qué está causando este efecto?

EDIT:

he cambiado de enfoque siguiente respuesta de Rob y adoptó el enfoque de tres método:

- (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's 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: %f y: %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]; 

} 

Aunque el efecto es ligeramente mejor, todavía hay un retraso notable con el rect finalmente arrastrando detrás de la dirección del movimiento del puntero del mouse. Esto es especialmente notable cuando muevo el mouse lentamente durante un arrastre.

EDIT 2:

Got it. El problema fue con el cálculo de los deltas. Utilicé mucho tiempo para eso mientras debería usar flotador. Funciona muy bien ahora.

+1

Gracias por compartir sus códigos para lograr que el mouse lo arrastre. – Tommy

Respuesta

9

Mantiene el bucle de evento durante el sorteo, lo que significa que el cuadrado nunca llega a volver a dibujarse. Su llamada al setNeedsDisplay: no dibuja nada. Simplemente marca la vista para volver a dibujar. No se puede volver a dibujar hasta que regrese esta rutina, lo que no se hace hasta que se suelte el botón del mouse.

Lea Handling Mouse Dragging Operations para obtener una discusión completa sobre cómo implementar el arrastre en Cocoa. O necesita regresar desde mouseDown: y anular mouseDragged: y mouseUp:, o necesita bombear el bucle de eventos usted mismo para que el ciclo de dibujo pueda procesarse.

Tiendo a recomendar el primer enfoque, aunque requiere múltiples métodos. El bombeo del bucle de evento puede crear errores muy sorprendentes y debe usarse con precaución. Los errores más comunes en mi experiencia se deben a los disparos de los selectores retrasados ​​cuando se bombea el bucle de eventos, lo que hace que el código "extra" se ejecute en el medio de la rutina de arrastre. En algunos casos, esto puede causar reentrada y punto muerto. (Tuve que pasar esto ...)

+0

+1 - definitivamente acuerde el primer enfoque mencionado. – bryanmac

Cuestiones relacionadas