From d7426f6d358269e7ffdfef0b219c6f2be943face Mon Sep 17 00:00:00 2001 From: Didier Korthoudt Date: Sat, 19 Dec 2015 21:56:27 +0100 Subject: [PATCH] Several fixes --- AppKit/CPControl.j | 1 - AppKit/CPView.j | 7 +- AppKit/CPWindow/CPWindow.j | 488 ++++++++++++++++++------------------- 3 files changed, 242 insertions(+), 254 deletions(-) diff --git a/AppKit/CPControl.j b/AppKit/CPControl.j index d4b7515b1..26aaca67d 100644 --- a/AppKit/CPControl.j +++ b/AppKit/CPControl.j @@ -1061,7 +1061,6 @@ var CPControlBlackColor = [CPColor blackColor]; - (void)updateTrackingAreas { [self removeAllTrackingAreas]; - [self addTrackingArea:[[CPTrackingArea alloc] initWithRect:CGRectMakeZero() options:CPTrackingMouseEnteredAndExited | CPTrackingActiveInKeyWindow | CPTrackingInVisibleRect owner:self diff --git a/AppKit/CPView.j b/AppKit/CPView.j index fbe77e33a..364cc8531 100644 --- a/AppKit/CPView.j +++ b/AppKit/CPView.j @@ -3464,7 +3464,7 @@ setBoundsOrigin: if (!trackingArea) return; - if (![_trackingAreas containsObject:trackingArea]) + if (![_trackingAreas containsObjectIdenticalTo:trackingArea]) [CPException raise:CPInternalInconsistencyException reason:"Trying to remove unreferenced trackingArea"]; [self _removeTrackingArea:trackingArea]; @@ -3518,7 +3518,6 @@ setBoundsOrigin: [_window _removeTrackingArea:trackingArea]; [trackingArea setView:nil]; - [_trackingAreas removeObjectIdenticalTo:trackingArea]; } @@ -3547,7 +3546,7 @@ setBoundsOrigin: var owners = []; - for (var i = 0, count = [_trackingAreas count]; i < count; i++) + for (var i = 0; i < _trackingAreas.length; i++) { var trackingArea = _trackingAreas[i]; @@ -3558,7 +3557,7 @@ setBoundsOrigin: { var owner = [trackingArea owner]; - if (![owners containsObject:owner]) + if (![owners containsObjectIdenticalTo:owner]) [owners addObject:owner]; } } diff --git a/AppKit/CPWindow/CPWindow.j b/AppKit/CPWindow/CPWindow.j index 7ffebc3ed..bb39d8719 100644 --- a/AppKit/CPWindow/CPWindow.j +++ b/AppKit/CPWindow/CPWindow.j @@ -202,7 +202,7 @@ var CPWindowActionMessageKeys = [ CPArray _cursorUpdateStack; CPArray _trackingAreaViews; id _activeCursorTrackingArea; - CPArray _delayedTrackingEvents; + CPArray _queuedTrackingEvents; CPView _leftMouseDownView; CPView _rightMouseDownView; @@ -343,7 +343,7 @@ CPTexturedBackgroundWindowMask _trackingAreaViews = []; _mouseEnteredStack = []; _cursorUpdateStack = []; - _delayedTrackingEvents = []; + _queuedTrackingEvents = []; _activeCursorTrackingArea = nil; // Create our border view which is the actual root of our view hierarchy. @@ -1940,7 +1940,7 @@ CPTexturedBackgroundWindowMask _leftMouseDownView = nil; // If mouseUp ends a drag operation, send delayed events for tracking views under the mouse, then flush delayed events - [self _flushDelayedTrackingEventsForMouseAt:point]; + [self _flushTrackingEventQueueForMouseAt:point]; return; @@ -3841,7 +3841,7 @@ var interpolate = function(fromValue, toValue, progress) { var trackingAreaView = [trackingArea view]; - if (![_trackingAreaViews containsObject:trackingAreaView]) + if (![_trackingAreaViews containsObjectIdenticalTo:trackingAreaView]) [_trackingAreaViews addObject:trackingAreaView]; // If CPTrackingAssumeInside option is set, put the tracking area in the _mouseEnteredStack @@ -3865,258 +3865,259 @@ var interpolate = function(fromValue, toValue, progress) { var mouseEnteredStack = [], cursorUpdateStack = [], - isKeyWindow = [self isKeyWindow], point = [anEvent locationInWindow], dragging = ([anEvent type] !== CPMouseMoved); + + // Handle mouse entering tracking areas (and calc mouseEnteredStack and cursorUpdateStack) + [self _handleMouseMovedAndEnteredEventsForEvent:anEvent atPoint:point dragging:dragging mouseEnteredStack:mouseEnteredStack cursorUpdateStack:cursorUpdateStack]; + + // Handle mouse exiting tracking areas + + [self _handleMouseExitedEventsForEvent:anEvent atPoint:point dragging:dragging mouseEnteredStack:mouseEnteredStack]; + + // Cursor update + + if (cursorUpdateStack.length > 0) + + [self _handleCursorUpdateEventsForEvent:anEvent atPoint:point dragging:dragging cursorUpdateStack:cursorUpdateStack]; + + else if (!dragging) + { + // Here, we are outsite the window content view tracking area, so let _windowView set the cursor (resize cursor, ...) + + [_windowView setCursorForLocation:point resizing:NO]; + _activeCursorTrackingArea = nil; + } + + // Prepare for next call + + _mouseEnteredStack = mouseEnteredStack; + _cursorUpdateStack = cursorUpdateStack; +} + +- (void)_handleMouseMovedAndEnteredEventsForEvent:(CPEvent)anEvent atPoint:(CGPoint)point dragging:(BOOL)dragging mouseEnteredStack:(CPArray)mouseEnteredStack cursorUpdateStack:(CPArray)cursorUpdateStack +{ + var isKeyWindow = [self isKeyWindow]; + for (var i = 0; i < _trackingAreaViews.length; i++) { var aView = _trackingAreaViews[i], trackingAreas = [aView trackingAreas]; - + if ([aView isHidden]) continue; - + for (var j = 0; j < trackingAreas.length; j++) { var aTrackingArea = trackingAreas[j], trackingOptions = [aTrackingArea options], trackingImplementedMethods = [aTrackingArea implementedOwnerMethods]; - if ((trackingOptions & CPTrackingActiveAlways) || - (trackingOptions & CPTrackingActiveInActiveApp) || - ((trackingOptions & CPTrackingActiveInKeyWindow) && isKeyWindow) || - ((trackingOptions & CPTrackingActiveWhenFirstResponder) && isKeyWindow && (_firstResponder === aView))) + if (!(((trackingOptions & CPTrackingActiveAlways) || + (trackingOptions & CPTrackingActiveInActiveApp) || + ((trackingOptions & CPTrackingActiveInKeyWindow) && isKeyWindow) || + ((trackingOptions & CPTrackingActiveWhenFirstResponder) && isKeyWindow && (_firstResponder === aView))) && + (CGRectContainsPoint([aTrackingArea windowRect], point)))) + + continue; + + [mouseEnteredStack addObject:aTrackingArea]; + + if ([_mouseEnteredStack containsObjectIdenticalTo:aTrackingArea]) { - if (!(CGRectContainsPoint([aTrackingArea windowRect], point))) - continue; - - [mouseEnteredStack addObject:aTrackingArea]; - - if ([_mouseEnteredStack containsObject:aTrackingArea]) - { - // Mouse was already in this rect so it's a mouseMoved - - if (!dragging && (trackingOptions & CPTrackingMouseMoved) && (trackingImplementedMethods & CPTrackingOwnerImplementsMouseMoved)) - [[aTrackingArea owner] mouseMoved:anEvent]; - } - else - { - // Mouse was not in this rect so it's a mouseEntered - - if ((trackingOptions & CPTrackingMouseEnteredAndExited) && (trackingImplementedMethods & CPTrackingOwnerImplementsMouseEntered)) - { - var mouseEnteredEvent = [CPEvent enterExitEventWithType:CPMouseEntered - location:point - modifierFlags:[anEvent modifierFlags] - timestamp:[anEvent timestamp] - windowNumber:_windowNumber - context:nil - eventNumber:-1 - trackingArea:aTrackingArea]; + // Mouse was already in this rect so it's a mouseMoved - if (dragging && !(trackingOptions & CPTrackingEnabledDuringMouseDrag)) - - [self _addEventToDelayedTrackingEvents:mouseEnteredEvent]; - - else - - [[aTrackingArea owner] mouseEntered:mouseEnteredEvent]; - } - } - - if ((trackingOptions & CPTrackingCursorUpdate) && (trackingImplementedMethods & CPTrackingOwnerImplementsCursorUpdate)) - [cursorUpdateStack addObject:aTrackingArea]; + if (!dragging && (trackingOptions & CPTrackingMouseMoved) && (trackingImplementedMethods & CPTrackingOwnerImplementsMouseMoved)) + [[aTrackingArea owner] mouseMoved:anEvent]; } - } - } - - // Search now for exited views (were in _mouseEnteredStack but no more in mouseEnteredStack) - - for (var i = 0; i < _mouseEnteredStack.length; i++) - { - var aTrackingArea = _mouseEnteredStack[i], - trackingOptions = [aTrackingArea options]; - - if (![mouseEnteredStack containsObject:aTrackingArea]) - { - // Mouse is no more in this area so it's a mouseExited - - if ((trackingOptions & CPTrackingMouseEnteredAndExited) && (trackingImplementedMethods & CPTrackingOwnerImplementsMouseExited)) + else if ((trackingOptions & CPTrackingMouseEnteredAndExited) && (trackingImplementedMethods & CPTrackingOwnerImplementsMouseEntered)) { - var mouseExitedEvent = [CPEvent enterExitEventWithType:CPMouseExited - location:point - modifierFlags:[anEvent modifierFlags] - timestamp:[anEvent timestamp] - windowNumber:_windowNumber - context:nil - eventNumber:-1 - trackingArea:aTrackingArea]; - - if (dragging && !(trackingOptions & CPTrackingEnabledDuringMouseDrag)) - - [self _addEventToDelayedTrackingEvents:mouseExitedEvent]; - - else - - [[aTrackingArea owner] mouseExited:mouseExitedEvent]; - } - - // If this is the active cursor area, we reset _cursorUpdateStack so a new active area will be computed - - if (aTrackingArea === _activeCursorTrackingArea) - { - _cursorUpdateStack = []; - _activeCursorTrackingArea = nil; - } - } - } - - _mouseEnteredStack = mouseEnteredStack; - - // Cursor update - - // We must send the cursorUpdate event to the frontmost tracking area (the nearest from the mouse pointer) - // If no frontmost tracking area is found, we set standard cursor - - var nbCursorUpdates = [cursorUpdateStack count], - overlappingTrackingAreas = []; - - if (nbCursorUpdates > 0) - { - for (var i = 0; i < nbCursorUpdates; i++) - { - var aTrackingArea = cursorUpdateStack[i]; - - if ((![_cursorUpdateStack containsObject:aTrackingArea]) || (aTrackingArea === _activeCursorTrackingArea)) - [overlappingTrackingAreas addObject:aTrackingArea]; - } - - var nbOverlappingTrackingAreas = overlappingTrackingAreas.length; - - if (nbOverlappingTrackingAreas > 0) - { - var frontmostTrackingArea = overlappingTrackingAreas[0], - frontmostView = [frontmostTrackingArea view]; - - for (var i = 1; i < nbOverlappingTrackingAreas; i++) - { - var aTrackingArea = overlappingTrackingAreas[i], - aView = [aTrackingArea view]; - - // First, if aView is _windowView, skip to next overlapping tracking area - // as _windowView can't be the frontmost view if there's multiple overlapping tracking areas. - - if (aView === _windowView) - continue; - - // Then, if frontmostView is _windowView, aView must become frontmostView - - if (frontmostView === _windowView) - { - frontmostTrackingArea = aTrackingArea; - frontmostView = aView; - - continue; - } - - // Next verify if aView is a subview of frontmostView - // If so, it's our new frontmost view - - var searchingView = aView; - - while ((searchingView !== _contentView) && ([searchingView superview] !== frontmostView)) - searchingView = [searchingView superview]; - - if (searchingView !== _contentView) - { - frontmostTrackingArea = aTrackingArea; - frontmostView = aView; - - continue; - } - - // aView is not a subview of frontmostView - // Search in view hierarchy which one will be over the other - // (this is done by comparing their draw order) - - var firstView = frontmostView, - firstSuperview = [firstView superview]; - - while (firstView !== _contentView) - { - var secondView = aView, - secondSuperview = [secondView superview]; - - while ((secondSuperview !== _contentView) && (firstSuperview !== secondSuperview)) - { - secondView = secondSuperview; - secondSuperview = [secondView superview]; - } - - if (firstSuperview === secondSuperview) - break; - - firstView = firstSuperview; - firstSuperview = [firstView superview]; - } - - if (firstSuperview !== secondSuperview) - [CPException raise:CPInternalInconsistencyException reason:"Problem with view hierarchy"]; - - var firstSuperviewSubviews = [firstSuperview subviews], - firstViewIndex = [firstSuperviewSubviews indexOfObject:firstView], - secondViewIndex = [firstSuperviewSubviews indexOfObject:secondView]; - - if (secondViewIndex > firstViewIndex) - { - frontmostTrackingArea = aTrackingArea; - frontmostView = aView; - } - } - - if (frontmostTrackingArea !== _activeCursorTrackingArea) - { - var cursorUpdateEvent = [CPEvent enterExitEventWithType:CPCursorUpdate + var mouseEnteredEvent = [CPEvent enterExitEventWithType:CPMouseEntered location:point modifierFlags:[anEvent modifierFlags] timestamp:[anEvent timestamp] windowNumber:_windowNumber context:nil eventNumber:-1 - trackingArea:frontmostTrackingArea]; + trackingArea:aTrackingArea]; if (dragging && !(trackingOptions & CPTrackingEnabledDuringMouseDrag)) - - [self _addEventToDelayedTrackingEvents:cursorUpdateEvent]; - + [self _queueTrackingEvent:mouseEnteredEvent]; else - - [[frontmostTrackingArea owner] cursorUpdate:cursorUpdateEvent]; - - _activeCursorTrackingArea = frontmostTrackingArea; + [[aTrackingArea owner] mouseEntered:mouseEnteredEvent]; } + + if ((trackingOptions & CPTrackingCursorUpdate) && (trackingImplementedMethods & CPTrackingOwnerImplementsCursorUpdate)) + [cursorUpdateStack addObject:aTrackingArea]; } } - else if (!dragging) - { - [_windowView setCursorForLocation:point resizing:NO]; - _activeCursorTrackingArea = nil; - } - - // End of treatment - - _cursorUpdateStack = cursorUpdateStack; } -- (void)_addEventToDelayedTrackingEvents:(CPEvent)anEvent +- (void)_handleMouseExitedEventsForEvent:(CPEvent)anEvent atPoint:(CGPoint)point dragging:(BOOL)dragging mouseEnteredStack:(CPArray)mouseEnteredStack { - // This will put a tracking event in the _delayedTrackingEvents queue. + // Search for exited views (were in _mouseEnteredStack but no more in mouseEnteredStack) + + for (var i = 0; i < _mouseEnteredStack.length; i++) + { + var aTrackingArea = _mouseEnteredStack[i], + trackingOptions = [aTrackingArea options]; + + if ([mouseEnteredStack containsObjectIdenticalTo:aTrackingArea]) + continue; + + // Mouse is no more in this area so it's a mouseExited + + if ((trackingOptions & CPTrackingMouseEnteredAndExited) && ([aTrackingArea implementedOwnerMethods] & CPTrackingOwnerImplementsMouseExited)) + { + var mouseExitedEvent = [CPEvent enterExitEventWithType:CPMouseExited + location:point + modifierFlags:[anEvent modifierFlags] + timestamp:[anEvent timestamp] + windowNumber:_windowNumber + context:nil + eventNumber:-1 + trackingArea:aTrackingArea]; + + if (dragging && !(trackingOptions & CPTrackingEnabledDuringMouseDrag)) + [self _queueTrackingEvent:mouseExitedEvent]; + else + [[aTrackingArea owner] mouseExited:mouseExitedEvent]; + } + + // If this is the active cursor area, we reset _cursorUpdateStack so a new active area will be computed + + if (aTrackingArea === _activeCursorTrackingArea) + { + _cursorUpdateStack = []; + _activeCursorTrackingArea = nil; + } + } +} + +- (void)_handleCursorUpdateEventsForEvent:(CPEvent)anEvent atPoint:(CGPoint)point dragging:(BOOL)dragging cursorUpdateStack:(CPArray)cursorUpdateStack +{ + var overlappingTrackingAreas = []; + + for (var i = 0; i < cursorUpdateStack.length; i++) + { + var aTrackingArea = cursorUpdateStack[i]; + + if ((![_cursorUpdateStack containsObjectIdenticalTo:aTrackingArea]) || (aTrackingArea === _activeCursorTrackingArea)) + [overlappingTrackingAreas addObject:aTrackingArea]; + } + + var nbOverlappingTrackingAreas = overlappingTrackingAreas.length; + + if (nbOverlappingTrackingAreas > 0) + { + var frontmostTrackingArea = overlappingTrackingAreas[0], + frontmostView = [frontmostTrackingArea view]; + + for (var i = 1; i < nbOverlappingTrackingAreas; i++) + { + var aTrackingArea = overlappingTrackingAreas[i], + aView = [aTrackingArea view]; + + // First, if aView is _windowView, skip to next overlapping tracking area + // as _windowView can't be the frontmost view if there's multiple overlapping tracking areas. + + if (aView === _windowView) + continue; + + // Then, if frontmostView is _windowView, aView must become frontmostView + + if (frontmostView === _windowView) + { + frontmostTrackingArea = aTrackingArea; + frontmostView = aView; + + continue; + } + + // Next verify if aView is a subview of frontmostView + // If so, it's our new frontmost view + + var searchingView = aView; + + while ((searchingView !== _contentView) && ([searchingView superview] !== frontmostView)) + searchingView = [searchingView superview]; + + if (searchingView !== _contentView) + { + frontmostTrackingArea = aTrackingArea; + frontmostView = aView; + + continue; + } + + // aView is not a subview of frontmostView + // Search in view hierarchy which one will be over the other + // (this is done by comparing their draw order) + + var firstView = frontmostView, + firstSuperview = [firstView superview]; + + while (firstView !== _contentView) + { + var secondView = aView, + secondSuperview = [secondView superview]; + + while ((secondSuperview !== _contentView) && (firstSuperview !== secondSuperview)) + { + secondView = secondSuperview; + secondSuperview = [secondView superview]; + } + + if (firstSuperview === secondSuperview) + break; + + firstView = firstSuperview; + firstSuperview = [firstView superview]; + } + + if (firstSuperview !== secondSuperview) + [CPException raise:CPInternalInconsistencyException reason:"Problem with view hierarchy"]; + + var firstSuperviewSubviews = [firstSuperview subviews], + firstViewIndex = [firstSuperviewSubviews indexOfObject:firstView], + secondViewIndex = [firstSuperviewSubviews indexOfObject:secondView]; + + if (secondViewIndex > firstViewIndex) + { + frontmostTrackingArea = aTrackingArea; + frontmostView = aView; + } + } + + if (frontmostTrackingArea !== _activeCursorTrackingArea) + { + var cursorUpdateEvent = [CPEvent enterExitEventWithType:CPCursorUpdate + location:point + modifierFlags:[anEvent modifierFlags] + timestamp:[anEvent timestamp] + windowNumber:_windowNumber + context:nil + eventNumber:-1 + trackingArea:frontmostTrackingArea]; + + if (dragging) + [self _queueTrackingEvent:cursorUpdateEvent]; + else + [[frontmostTrackingArea owner] cursorUpdate:cursorUpdateEvent]; + + _activeCursorTrackingArea = frontmostTrackingArea; + } + } +} + +- (void)_queueTrackingEvent:(CPEvent)anEvent +{ + // This will put a tracking event in the _queuedTrackingEvents queue. // // We optimize this queue with this policy : // - if mouseEntered, search if queue contains a previous mouseExited for the same tracking area. If so, discard both. // - if mouseExited, search if queue contains a previous mouseEntered for the same tracking area. If so, discard both. - // - if cursorUpdate, search if queue contains a previous cursorUpdate for whatever tracking area. If so, discard previous one. // // This is not Cocoa way of doing as it would send every event. // But final result should be the same. @@ -4127,81 +4128,70 @@ var interpolate = function(fromValue, toValue, progress) switch ([anEvent type]) { case CPMouseEntered: - - for (var i = 0, count = _delayedTrackingEvents.length; i < count; i++) + for (var i = 0; i < _queuedTrackingEvents.length; i++) { - var delayedEvent = _delayedTrackingEvents[i]; + var queuedEvent = _queuedTrackingEvents[i]; - if (([delayedEvent trackingArea] === trackingArea) && ([delayedEvent type] === CPMouseExited)) + if (([queuedEvent trackingArea] === trackingArea) && ([queuedEvent type] === CPMouseExited)) { - [_delayedTrackingEvents removeObjectAtIndex:i]; - + [_queuedTrackingEvents removeObjectAtIndex:i]; return; } } - [_delayedTrackingEvents addObject:anEvent]; - + [_queuedTrackingEvents addObject:anEvent]; break; case CPMouseExited: - - for (var i = 0, count = _delayedTrackingEvents.length; i < count; i++) + for (var i = 0; i < _queuedTrackingEvents.length; i++) { - var delayedEvent = _delayedTrackingEvents[i]; + var queuedEvent = _queuedTrackingEvents[i]; - if (([delayedEvent trackingArea] === trackingArea) && ([delayedEvent type] === CPMouseEntered)) + if (([queuedEvent trackingArea] === trackingArea) && ([queuedEvent type] === CPMouseEntered)) { - [_delayedTrackingEvents removeObjectAtIndex:i]; - + [_queuedTrackingEvents removeObjectAtIndex:i]; return; } } - [_delayedTrackingEvents addObject:anEvent]; - + [_queuedTrackingEvents addObject:anEvent]; break; case CPCursorUpdate: - - [_delayedTrackingEvents addObject:anEvent]; - + [_queuedTrackingEvents addObject:anEvent]; break; } } -- (void)_flushDelayedTrackingEventsForMouseAt:(CGPoint)point +- (void)_flushTrackingEventQueueForMouseAt:(CGPoint)point { - for (var i = 0, count = _delayedTrackingEvents.length; i < count; i++) + for (var i = 0; i < _queuedTrackingEvents.length; i++) { - var delayedEvent = _delayedTrackingEvents[i], - trackingArea = [delayedEvent trackingArea], + var queuedEvent = _queuedTrackingEvents[i], + trackingArea = [queuedEvent trackingArea], trackingOwner = [trackingArea owner]; - switch ([delayedEvent type]) + switch ([queuedEvent type]) { case CPMouseEntered: - - [trackingOwner mouseEntered:delayedEvent]; + [trackingOwner mouseEntered:queuedEvent]; break; case CPMouseExited: - - [trackingOwner mouseExited:delayedEvent]; + [trackingOwner mouseExited:queuedEvent]; break; case CPCursorUpdate: - [trackingOwner updateTrackingAreas]; if (CGRectContainsPoint([trackingArea windowRect], point)) - [trackingOwner cursorUpdate:delayedEvent]; + [trackingOwner cursorUpdate:queuedEvent]; break; } } - _delayedTrackingEvents = []; + _queuedTrackingEvents = []; } @end