From 08d4e1985161321f9ed3f6d57e85f75f1bc016f7 Mon Sep 17 00:00:00 2001 From: Didier Korthoudt Date: Mon, 7 Dec 2015 14:58:42 +0100 Subject: [PATCH] various-fixes Following @aparajita indications --- AppKit/CPResponder.j | 13 ++++ AppKit/CPTableHeaderView.j | 9 --- AppKit/CPTrackingArea.j | 44 ++++++++--- AppKit/CPView.j | 156 ++++++++++++++++++------------------- AppKit/CPWindow/CPWindow.j | 29 +++---- 5 files changed, 139 insertions(+), 112 deletions(-) diff --git a/AppKit/CPResponder.j b/AppKit/CPResponder.j index eddf8b628..eb1bc841c 100644 --- a/AppKit/CPResponder.j +++ b/AppKit/CPResponder.j @@ -24,6 +24,7 @@ @import @import "CPEvent.j" +@import "CPCursor.j" @class CPKeyBinding @class CPMenu @@ -200,6 +201,18 @@ CPDeleteForwardKeyCode = 46; [_nextResponder performSelector:_cmd withObject:anEvent]; } +/*! + Notifies the receiver that the mouse entered the receiver's area and that it can adapt the cursor. + @param anEvent contains information about the exit + */ +- (void)cursorUpdate:(CPEvent)anEvent +{ + if (_nextResponder) + [_nextResponder performSelector:_cmd withObject:anEvent]; + else + [[CPCursor arrowCursor] set]; +} + /*! Notifies the receiver that the mouse scroll wheel has moved. @param anEvent information about the scroll diff --git a/AppKit/CPTableHeaderView.j b/AppKit/CPTableHeaderView.j index c70aba33c..79e76a786 100644 --- a/AppKit/CPTableHeaderView.j +++ b/AppKit/CPTableHeaderView.j @@ -692,7 +692,6 @@ var CPTableHeaderViewResizeZone = 3.0, [[_tableView headerView] setNeedsLayout]; [[CPCursor arrowCursor] set]; - [self updateTrackingAreas]; } @@ -761,7 +760,6 @@ var CPTableHeaderViewResizeZone = 3.0, if ([tableColumn width] != _columnOldWidth) { [_tableView _didResizeTableColumn:tableColumn oldWidth:_columnOldWidth]; - [self updateTrackingAreas]; } @@ -773,13 +771,6 @@ var CPTableHeaderViewResizeZone = 3.0, - (void)_updateResizeCursor:(CPEvent)theEvent { - // never get stuck in resize cursor mode (FIXME take out when we turn on tracking rects) - if (![_tableView allowsColumnResizing] || ([theEvent type] === CPLeftMouseUp && ![[self window] acceptsMouseMovedEvents])) - { - [[CPCursor arrowCursor] set]; - return; - } - var mouseLocation = [self convertPoint:[theEvent locationInWindow] fromView:nil], mouseOverLocation = CGPointMake(MAX(mouseLocation.x - CPTableHeaderViewResizeZone, 0.0), mouseLocation.y), overColumn = [self columnAtPoint:mouseOverLocation]; diff --git a/AppKit/CPTrackingArea.j b/AppKit/CPTrackingArea.j index 13b78280a..1cb19bf96 100644 --- a/AppKit/CPTrackingArea.j +++ b/AppKit/CPTrackingArea.j @@ -21,7 +21,8 @@ */ @import -@import "CPView.j" + +@class CPView /* @group CPTrackingAreaOptions */ @typedef CPTrackingAreaOptions @@ -43,6 +44,10 @@ var CPTrackingAreaViewRectKey = @"CPTrackinkAreaViewRectKey", CPTrackingAreaReferencingViewKey = @"CPTrackingAreaReferencingViewKey", CPTrackingAreaWindowRect = @"CPTrackingAreaWindowRect"; +CPTrackingOwnerImplementsMouseEntered = 1 << 1; +CPTrackingOwnerImplementsMouseExited = 1 << 2; +CPTrackingOwnerImplementsMouseMoved = 1 << 3; +CPTrackingOwnerImplementsCursorUpdate = 1 << 4; /*! @ingroup appkit @@ -52,25 +57,32 @@ var CPTrackingAreaViewRectKey = @"CPTrackinkAreaViewRectKey", */ @implementation CPTrackingArea : CPObject { - CGRect _viewRect @accessors(getter=rect); - CPTrackingAreaOptions _options @accessors(getter=options); - id _owner @accessors(getter=owner); - CPDictionary _userInfo @accessors(getter=userInfo); + CGRect _viewRect @accessors(getter=rect); + CPTrackingAreaOptions _options @accessors(getter=options); + id _owner @accessors(getter=owner); + CPDictionary _userInfo @accessors(getter=userInfo); - CPView _referencingView @accessors(property=view); - CGRect _windowRect @accessors(getter=actualRect); + CPView _referencingView @accessors(property=view); + CGRect _windowRect @accessors(getter=actualRect); + + unsigned _implementedOwnerMethods @accessors(getter=implementedOwnerMethods); } #pragma mark - #pragma mark Initialization -/*! Initializes and returns an object defining a region of a view to receive mouse-tracking events, mouse-moved events, cursor-update events, or possibly all these events. +/*! + Initializes and returns an object defining a region of a view to receive mouse-tracking events, mouse-moved events, cursor-update events, or possibly + all these events. */ - (CPTrackingArea)initWithRect:(CGRect)aRect options:(CPTrackingAreaOptions)options owner:(id)owner userInfo:(CPDictionary)userInfo { if (self = [super init]) { + if (owner === nil) + [CPException raise:CPInternalInconsistencyException reason:"No owner specified"]; + if (options === 0) [CPException raise:CPInternalInconsistencyException reason:"Invalid CPTrackingArea options"]; @@ -89,6 +101,20 @@ var CPTrackingAreaViewRectKey = @"CPTrackinkAreaViewRectKey", _options = options; _owner = owner; _userInfo = userInfo; + + // Cache owner implemented methods + + if ([_owner respondsToSelector:@selector(mouseEntered:)]) + _implementedOwnerMethods |= CPTrackingOwnerImplementsMouseEntered; + + if ([_owner respondsToSelector:@selector(mouseExited:)]) + _implementedOwnerMethods |= CPTrackingOwnerImplementsMouseExited; + + if ([_owner respondsToSelector:@selector(mouseMoved:)]) + _implementedOwnerMethods |= CPTrackingOwnerImplementsMouseMoved; + + if ([_owner respondsToSelector:@selector(cursorUpdate:)]) + _implementedOwnerMethods |= CPTrackingOwnerImplementsCursorUpdate; } return self; @@ -98,7 +124,7 @@ var CPTrackingAreaViewRectKey = @"CPTrackinkAreaViewRectKey", #pragma mark - #pragma mark Implementation -- (void)_updateActualRect +- (void)_updateWindowRect { _windowRect = [_referencingView convertRect:((_options & CPTrackingInVisibleRect) ? [_referencingView visibleRect] : _viewRect) toView:[[_referencingView window] _windowView]]; } diff --git a/AppKit/CPView.j b/AppKit/CPView.j index 21905c39b..fbe77e33a 100644 --- a/AppKit/CPView.j +++ b/AppKit/CPView.j @@ -242,9 +242,7 @@ var CPViewHighDPIDrawingEnabled = YES; CPAppearance _effectiveAppearance; CPMutableArray _trackingAreas @accessors(getter=trackingAreas, copy); - BOOL _inhibitUpdateTrackingAreasNotifications; -// BOOL _postsUpdateTrackingAreas; -// CGRect _previousVisibleRect; + BOOL _inhibitUpdateTrackingAreas; } /* @@ -814,14 +812,23 @@ var CPViewHighDPIDrawingEnabled = YES; _window = aWindow; - // View must be added to the new window viewsWithTrackingAreas - if (_window && (_trackingAreas.length > 0)) - [_window _addTrackingAreaView:self]; - - // Notify that view tracking areas should be updated - // Cocoa doesn't notify on leaving a window if (_window) - [self _notifyUpdateTrackingAreasForSetWindow]; + { + var owners; + + if (_trackingAreas.length > 0) + { + // View must be added to the new window viewsWithTrackingAreas + [_window _addTrackingAreaView:self]; + owners = [self _calcTrackingAreaOwners]; + } + else + owners = [self]; + + // Notify that view tracking areas should be updated + // Cocoa doesn't notify on leaving a window + [self _updateTrackingAreasForOwners:owners]; + } var count = [_subviews count]; @@ -1016,8 +1023,8 @@ var CPViewHighDPIDrawingEnabled = YES; if (_isSuperviewAClipView) [[self superview] viewFrameChanged:[[CPNotification alloc] initWithName:CPViewFrameDidChangeNotification object:self userInfo:nil]]; - if (!_inhibitUpdateTrackingAreasNotifications) - [self _notifyUpdateTrackingAreas]; + if (!_inhibitUpdateTrackingAreas) + [self _updateTrackingAreas]; } /*! @@ -1089,8 +1096,8 @@ var CPViewHighDPIDrawingEnabled = YES; CPDOMDisplayServerSetStyleLeftTop(_DOMElement, transform, origin.x, origin.y); #endif - if (!_inhibitUpdateTrackingAreasNotifications && !_inhibitFrameAndBoundsChangedNotifications) - [self _notifyUpdateTrackingAreas]; + if (!_inhibitUpdateTrackingAreas && !_inhibitFrameAndBoundsChangedNotifications) + [self _updateTrackingAreas]; } /*! @@ -1243,8 +1250,8 @@ var CPViewHighDPIDrawingEnabled = YES; if (_isSuperviewAClipView && !_inhibitFrameAndBoundsChangedNotifications) [[self superview] viewFrameChanged:[[CPNotification alloc] initWithName:CPViewFrameDidChangeNotification object:self userInfo:nil]]; - if (!_inhibitUpdateTrackingAreasNotifications && !_inhibitFrameAndBoundsChangedNotifications) - [self _notifyUpdateTrackingAreas]; + if (!_inhibitUpdateTrackingAreas && !_inhibitFrameAndBoundsChangedNotifications) + [self _updateTrackingAreas]; } /*! @@ -1292,8 +1299,8 @@ var CPViewHighDPIDrawingEnabled = YES; if (_isSuperviewAClipView) [[self superview] viewBoundsChanged:[[CPNotification alloc] initWithName:CPViewBoundsDidChangeNotification object:self userInfo:nil]]; - if (!_inhibitUpdateTrackingAreasNotifications) - [self _notifyUpdateTrackingAreas]; + if (!_inhibitUpdateTrackingAreas) + [self _updateTrackingAreas]; } /*! @@ -1360,8 +1367,8 @@ var CPViewHighDPIDrawingEnabled = YES; if (_isSuperviewAClipView && !_inhibitFrameAndBoundsChangedNotifications) [[self superview] viewBoundsChanged:[[CPNotification alloc] initWithName:CPViewBoundsDidChangeNotification object:self userInfo:nil]]; - if (!_inhibitUpdateTrackingAreasNotifications && !_inhibitFrameAndBoundsChangedNotifications) - [self _notifyUpdateTrackingAreas]; + if (!_inhibitUpdateTrackingAreas && !_inhibitFrameAndBoundsChangedNotifications) + [self _updateTrackingAreas]; } /*! @@ -1404,8 +1411,8 @@ var CPViewHighDPIDrawingEnabled = YES; if (_isSuperviewAClipView && !_inhibitFrameAndBoundsChangedNotifications) [[self superview] viewBoundsChanged:[[CPNotification alloc] initWithName:CPViewBoundsDidChangeNotification object:self userInfo:nil]]; - if (!_inhibitUpdateTrackingAreasNotifications && !_inhibitFrameAndBoundsChangedNotifications) - [self _notifyUpdateTrackingAreas]; + if (!_inhibitUpdateTrackingAreas && !_inhibitFrameAndBoundsChangedNotifications) + [self _updateTrackingAreas]; } @@ -3448,7 +3455,7 @@ setBoundsOrigin: if (_window) [_window _addTrackingArea:trackingArea]; - [trackingArea _updateActualRect]; + [trackingArea _updateWindowRect]; } - (void)removeTrackingArea:(CPTrackingArea)trackingArea @@ -3463,37 +3470,42 @@ setBoundsOrigin: [self _removeTrackingArea:trackingArea]; } -// Invoked automatically when the view’s geometry changes such that its tracking areas need to be recalculated. +/*! + Invoked automatically when the view’s geometry changes such that its tracking areas need to be recalculated. + You should override this method to remove out of date tracking areas and add recomputed tracking areas; + + Cocoa calls this on every view, whereas they have tracking area(s) or not. + Cappuccino behaves differently : + - updateTrackingAreas is called when placing a view in the view hierarchy (that is in a window) + - if you have only CPTrackingInVisibleRect tracking areas attached to a view, it will not be called again (until you move the view in the hierarchy) + - if you have at least one non-CPTrackingInVisibleRect tracking area attached, it will be called every time the view geometry could be modified + You don't have to touch to CPTrackingInVisibleRect tracking areas, they will be automatically updated + + Please note that it is the owner of a tracking area who is called for updateTrackingAreas. + But, if a view without any tracking area is inserted in the view hierarchy (that is, in a window), the view is called for updateTrackingAreas. + This enables you to use updateTrackingArea to initially attach your tracking areas to the view. +*/ - (void)updateTrackingAreas { - // You should override this method to remove out of date tracking areas and add recomputed tracking areas; - - // Cocoa calls this on every view, whereas they have tracking area(s) or not. - // Cappuccino behaves differently : - // - updateTrackingAreas is called when placing a view in the view hierarchy (that is in a window) - // - if you have only CPTrackingInVisibleRect tracking areas attached to a view, it will not be called again (until you move the view in the hierarchy) - // - if you have at least one non-CPTrackingInVisibleRect tracking area attached, it will be called every time the view geometry could be modified - // You don't have to touch to CPTrackingInVisibleRect tracking areas, they will be automatically updated - // - // Please note that it is the owner of a tracking area who is called for updateTrackingAreas. - // But, if a view without any tracking area is inserted in the view hierarchy (that is, in a window), the view is called for updateTrackingAreas. - // This enables you to use updateTrackingArea to initially attach your tracking areas to the view. + } +/*! + This utility method is intended for CPView subclasses overriding updateTrackingAreas + + Typical use would be : + + - (void)updateTrackingAreas + { + [self removeAllTrackingAreas]; + + ... add your specific updated tracking areas ... + } + +*/ - (void)removeAllTrackingAreas { - // This utility method is intended for CPView subclasses overwriting updateTrackingAreas - // - // Typical use would be : - // - // - (void)updateTrackingAreas - // { - // [self removeAllTrackingAreas]; - // - // ... add your specific updated tracking areas ... - // } - while (_trackingAreas.length > 0) [self _removeTrackingArea:_trackingAreas[0]]; } @@ -3510,70 +3522,54 @@ setBoundsOrigin: [_trackingAreas removeObjectIdenticalTo:trackingArea]; } -- (void)_notifyUpdateTrackingAreas +- (void)_updateTrackingAreas { - _inhibitUpdateTrackingAreasNotifications = YES; + _inhibitUpdateTrackingAreas = YES; - [self _recursiveNotifyUpdateTrackingAreas]; + [self _recursivelyUpdateTrackingAreas]; - _inhibitUpdateTrackingAreasNotifications = NO; + _inhibitUpdateTrackingAreas = NO; } -- (void)_recursiveNotifyUpdateTrackingAreas +- (void)_recursivelyUpdateTrackingAreas { - [self _notifyOwners:[self _prepareOwnersToNotify]]; - - // Recursive call for all subviews + [self _updateTrackingAreasForOwners:[self _calcTrackingAreaOwners]]; for (var i = 0; i < _subviews.length; i++) - [_subviews[i] _recursiveNotifyUpdateTrackingAreas]; + [_subviews[i] _recursivelyUpdateTrackingAreas]; } -- (void)_notifyUpdateTrackingAreasForSetWindow -{ - // As _setWindow will call recursively itself for subviews, we only have to deal with the view itself - // We also do special treatment and notify the view itself if it has no tracking area to - // enable the use of updateTrackingAreas as a mean to install tracking areas. - - if ([_trackingAreas count] > 0) - var ownersToNotify = [self _prepareOwnersToNotify]; - else - var ownersToNotify = @[ self ]; - - [self _notifyOwners:ownersToNotify] -} - -- (CPArray)_prepareOwnersToNotify +- (CPArray)_calcTrackingAreaOwners { // First search all owners that must be notified // Remark: 99.99% of time, the only owner will be the view itself // In the same time, update the rects of InVisibleRect tracking areas - var ownersToNotify = []; + var owners = []; for (var i = 0, count = [_trackingAreas count]; i < count; i++) { var trackingArea = _trackingAreas[i]; if ([trackingArea options] & CPTrackingInVisibleRect) - [trackingArea _updateActualRect]; + [trackingArea _updateWindowRect]; else { var owner = [trackingArea owner]; - if (![ownersToNotify containsObject:owner]) - [ownersToNotify addObject:owner]; + if (![owners containsObject:owner]) + [owners addObject:owner]; } } - return ownersToNotify; + return owners; } -- (void)_notifyOwners:(CPArray)ownersToNotify +- (void)_updateTrackingAreasForOwners:(CPArray)owners { - for (var i = 0; i < ownersToNotify.length; i++) - [ownersToNotify[i] updateTrackingAreas]; + for (var i = 0; i < owners.length; i++) + [owners[i] updateTrackingAreas]; } @end @@ -3703,7 +3699,7 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask", [self _decodeThemeObjectsWithCoder:aCoder]; [self setAppearance:[aCoder decodeObjectForKey:CPViewAppearanceKey]]; - + [self setNeedsDisplay:YES]; [self setNeedsLayout]; } diff --git a/AppKit/CPWindow/CPWindow.j b/AppKit/CPWindow/CPWindow.j index fc8c3f5f9..e0e5eb5f7 100644 --- a/AppKit/CPWindow/CPWindow.j +++ b/AppKit/CPWindow/CPWindow.j @@ -1974,7 +1974,7 @@ CPTexturedBackgroundWindowMask case CPRightMouseDragged: // First, we search for any tracking area requesting CPTrackingEnabledDuringMouseDrag. // At the same time, we update the entered stack. - [self _trackEvent:anEvent]; + [self _handleTrackingAreaEvent:anEvent]; // Normal mouseDragged workflow if (!_leftMouseDownView) @@ -2000,7 +2000,7 @@ CPTexturedBackgroundWindowMask if (!_acceptsMouseMovedEvents || sheet) return; - [self _trackEvent:anEvent]; + [self _handleTrackingAreaEvent:anEvent]; } } @@ -3856,7 +3856,7 @@ var interpolate = function(fromValue, toValue, progress) [_trackingAreaViews removeObjectIdenticalTo:trackingAreaView]; } -- (void)_trackEvent:(CPEvent)anEvent +- (void)_handleTrackingAreaEvent:(CPEvent)anEvent { var mouseEnteredStack = [], cursorUpdateStack = [], @@ -3874,8 +3874,9 @@ var interpolate = function(fromValue, toValue, progress) for (var j = 0; j < trackingAreas.length; j++) { - var aTrackingArea = trackingAreas[j], - trackingOptions = [aTrackingArea options]; + var aTrackingArea = trackingAreas[j], + trackingOptions = [aTrackingArea options], + trackingImplementedMethods = [aTrackingArea implementedOwnerMethods]; if (dragging && !(trackingOptions & CPTrackingEnabledDuringMouseDrag)) continue; @@ -3894,14 +3895,14 @@ var interpolate = function(fromValue, toValue, progress) { // Mouse was already in this rect so it's a mouseMoved - if (!dragging && (trackingOptions & CPTrackingMouseMoved)) + 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) + if ((trackingOptions & CPTrackingMouseEnteredAndExited) && (trackingImplementedMethods & CPTrackingOwnerImplementsMouseEntered)) [[aTrackingArea owner] mouseEntered:[CPEvent enterExitEventWithType:CPMouseEntered location:point modifierFlags:[anEvent modifierFlags] @@ -3912,7 +3913,7 @@ var interpolate = function(fromValue, toValue, progress) trackingArea:aTrackingArea]]; } - if (trackingOptions & CPTrackingCursorUpdate) + if ((trackingOptions & CPTrackingCursorUpdate) && (trackingImplementedMethods & CPTrackingOwnerImplementsCursorUpdate)) [cursorUpdateStack addObject:aTrackingArea]; } } @@ -3929,7 +3930,7 @@ var interpolate = function(fromValue, toValue, progress) { // Mouse is no more in this area so it's a mouseExited - if ((trackingOptions & CPTrackingMouseEnteredAndExited) && (!dragging || (dragging && (trackingOptions & CPTrackingEnabledDuringMouseDrag)))) + if ((trackingOptions & CPTrackingMouseEnteredAndExited) && (!dragging || (dragging && (trackingOptions & CPTrackingEnabledDuringMouseDrag))) && (trackingImplementedMethods & CPTrackingOwnerImplementsMouseExited)) [[aTrackingArea owner] mouseExited:[CPEvent enterExitEventWithType:CPMouseExited location:point modifierFlags:[anEvent modifierFlags] @@ -3958,8 +3959,8 @@ var interpolate = function(fromValue, toValue, progress) // Cursor update - // We must send the cursorUpdate event to the frontmost view (the nearest from the mouse pointer) - // If no frontmost view is found, we set standard cursor + // 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 = []; @@ -3971,7 +3972,6 @@ var interpolate = function(fromValue, toValue, progress) var aTrackingArea = cursorUpdateStack[i]; if ((![_cursorUpdateStack containsObject:aTrackingArea]) || (aTrackingArea === _activeCursorTrackingArea)) - [overlappingTrackingAreas addObject:aTrackingArea]; } @@ -4031,8 +4031,9 @@ var interpolate = function(fromValue, toValue, progress) if (firstSuperview !== secondSuperview) [CPException raise:CPInternalInconsistencyException reason:"Problem with view hierarchy"]; - var firstViewIndex = [[firstSuperview subviews] indexOfObject:firstView], - secondViewIndex = [[firstSuperview subviews] indexOfObject:secondView]; + var firstSuperviewSubviews = [firstSuperview subviews], + firstViewIndex = [firstSuperviewSubviews indexOfObject:firstView], + secondViewIndex = [firstSuperviewSubviews indexOfObject:secondView]; if (secondViewIndex > firstViewIndex) {