movimiento mousereleased eventos evento detectar codigo objective-c macos cocoa nstrackingarea

objective c - mousereleased - mouseExited no se llama cuando el mouse sale de trackingArea mientras se desplaza



mouse adapter java (2)

¿Por qué mouseExited / mouseEntered no se llama cuando el mouse sale de NStrackingArea desplazándose o haciendo animación?

Creo código como este:

Ratón entrado y salido:

-(void)mouseEntered:(NSEvent *)theEvent { NSLog(@"Mouse entered"); } -(void)mouseExited:(NSEvent *)theEvent { NSLog(@"Mouse exited"); }

Área de seguimiento:

-(void)updateTrackingAreas { if(trackingArea != nil) { [self removeTrackingArea:trackingArea]; [trackingArea release]; } int opts = (NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways); trackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds] options:opts owner:self userInfo:nil]; [self addTrackingArea:trackingArea]; }

Más detalles:

He agregado NSViews como subvistas en la vista de NSScrollView. Cada NSView tiene su propia área de seguimiento y cuando desplazo mi scrollView y dejo el área de seguimiento "mouseExited" no se llama, pero sin el desplazamiento todo funciona bien. El problema es que cuando me desplazo, se llama "updateTrackingAreas" y creo que esto genera problemas.

* El mismo problema con solo NSView sin agregarlo como subvista, por lo que no es un problema.


@Michael ofrece una gran respuesta, y resolvió mi problema. Pero hay una cosa,

if (CGRectContainsPoint([self bounds], mouseLocation)) { [self mouseEntered: nil]; } else { [self mouseExited: nil]; }

Encontré que CGRectContainsPoint funciona en mi caja, no CGPointInRect ,


Como anotó en el título de la pregunta, mouseEntered y mouseExited solo se llaman cuando el mouse se mueve. Para ver por qué este es el caso, primero veamos el proceso de agregar NSTrackingAreas por primera vez.

Como ejemplo simple, vamos a crear una vista que normalmente dibuja un fondo blanco, pero si el usuario se desplaza sobre la vista, dibuja un fondo rojo. Este ejemplo utiliza ARC.

@interface ExampleView - (void) createTrackingArea @property (nonatomic, retain) backgroundColor; @property (nonatomic, retain) trackingArea; @end @implementation ExampleView @synthesize backgroundColor; @synthesize trackingArea - (id) awakeFromNib { [self setBackgroundColor: [NSColor whiteColor]]; [self createTrackingArea]; } - (void) createTrackingArea { int opts = (NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways); trackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds] options:opts owner:self userInfo:nil]; [self addTrackingArea:trackingArea]; } - (void) drawRect: (NSRect) rect { [[self backgroundColor] set]; NSRectFill(rect); } - (void) mouseEntered: (NSEvent*) theEvent { [self setBackgroundColor: [NSColor redColor]]; } - (void) mouseEntered: (NSEvent*) theEvent { [self setBackgroundColor: [NSColor whiteColor]]; } @end

Hay dos problemas con este código. Primero, cuando se llama a -awakeFromNib, si el mouse ya está dentro de la vista, no se llama a -mouseEntered. Esto significa que el fondo seguirá siendo blanco, aunque el mouse esté sobre la vista. Esto se menciona en realidad en la documentación de NSView para el parámetro assumeInside de -addTrackingRect: owner: userData: assumeInside:

Si es SÍ, el primer evento se generará cuando el cursor salga de aRect, independientemente de si el cursor está dentro de aRect cuando se agrega el rectángulo de seguimiento. Si NO, el primer evento se generará cuando el cursor salga de aRect si el cursor está inicialmente dentro de aRect, o cuando el cursor entre aRect si el cursor está inicialmente fuera de aRect.

En ambos casos, si el mouse está dentro del área de rastreo, no se generarán eventos hasta que el mouse abandone el área de rastreo.

Entonces, para solucionar esto, cuando agregamos el área de seguimiento, necesitamos averiguar si el cursor está dentro del área de seguimiento. Nuestro método -createTrackingArea se convierte así en

- (void) createTrackingArea { int opts = (NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways); trackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds] options:opts owner:self userInfo:nil]; [self addTrackingArea:trackingArea]; NSPoint mouseLocation = [[self window] mouseLocationOutsideOfEventStream]; mouseLocation = [self convertPoint: mouseLocation fromView: nil]; if (NSPointInRect(mouseLocation, [self bounds])) { [self mouseEntered: nil]; } else { [self mouseExited: nil]; } }

El segundo problema es el desplazamiento. Al desplazar o mover una vista, debemos volver a calcular NSTrackingAreas en esa vista. Esto se hace eliminando las áreas de seguimiento y luego agregándolas de nuevo. Como anotó, se llama a -updateTrackingAreas cuando se desplaza la vista. Este es el lugar para eliminar y volver a agregar el área.

- (void) updateTrackingAreas { [self removeTrackingArea:trackingArea]; [self createTrackingArea]; [super updateTrackingAreas]; // Needed, according to the NSView documentation }

Y eso debería solucionar su problema. Es cierto que la necesidad de encontrar la ubicación del mouse y luego convertirla para ver las coordenadas cada vez que agrega un área de rastreo es algo que envejece rápidamente, por lo que recomendaría crear una categoría en NSView que maneje esto automáticamente. No siempre podrá llamar a [self mouseEntered: nil] o [self mouseExited: nil], por lo que es posible que desee que la categoría acepte un par de bloques. Una para ejecutar si el mouse está en NSTrackingArea, y otra para ejecutar si no lo está.