Files
cappuccino/AppKit/CPTrackingArea.j
T
Martin CarlbergandGitHub 109003ae12 Fixed: Performance improvement on visibleRect when updating tracking areas (#2881)
Calculations of the visible rect can be a heavy operation as it depends of the superviews
visible rect. They have to be transformed and intersected together to create the result.
This calculation is a recursive operation that travels up the view hierarchy all the way
to the top.

Updating the tracking areas is also a recursive operation. But it travels down the view hierarchy
instead. It will calculate the new tracking area using the visible rect for the current view. For
each view it updates it will get the visible rect that has to travel all the way up the view
hierarchy to calculate it by transform and intersect them together. This results in that the visible
rect will be calculated for each view multible times. This makes it a very slow operation, specially
for deeper view hierarchies.

This fix will send along the visible rect for the superview when traveling down the
view hierarchy. This means that the visible rect does not need to travel up the view hierarchy
to be calculated. It will just need transform and intersect the provided rect for its superview
with the one from itself.
2020-01-06 12:48:40 +01:00

174 lines
7.1 KiB
Plaintext

/*
* CPTrackingArea.j
* AppKit
*
* Created by Didier Korthoudt.
* Copyright 2015, Cappuccino Project.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import <Foundation/Foundation.j>
@class CPView
/* @group CPTrackingAreaOptions */
@typedef CPTrackingAreaOptions
CPTrackingMouseEnteredAndExited = 1 << 1;
CPTrackingMouseMoved = 1 << 2;
CPTrackingCursorUpdate = 1 << 3;
CPTrackingActiveWhenFirstResponder = 1 << 4;
CPTrackingActiveInKeyWindow = 1 << 5;
CPTrackingActiveInActiveApp = 1 << 6;
CPTrackingActiveAlways = 1 << 7;
CPTrackingAssumeInside = 1 << 8;
CPTrackingInVisibleRect = 1 << 9;
CPTrackingEnabledDuringMouseDrag = 1 << 10;
var CPTrackingAreaViewRectKey = @"CPTrackinkAreaViewRectKey",
CPTrackingAreaOptionsKey = @"CPTrackingAreaOptionsKey",
CPTrackingAreaOwnerKey = @"CPTrackingAreaOwnerKey",
CPTrackingAreaUserInfoKey = @"CPTrackingAreaUserInfoKey",
CPTrackingAreaReferencingViewKey = @"CPTrackingAreaReferencingViewKey",
CPTrackingAreaWindowRect = @"CPTrackingAreaWindowRect";
CPTrackingOwnerImplementsMouseEntered = 1 << 1;
CPTrackingOwnerImplementsMouseExited = 1 << 2;
CPTrackingOwnerImplementsMouseMoved = 1 << 3;
CPTrackingOwnerImplementsCursorUpdate = 1 << 4;
/*!
@ingroup appkit
A CPTrackingArea defines a region of view that generates mouse-tracking and
cursor-update events when the mouse is over that region.
*/
@implementation CPTrackingArea : CPObject
{
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=windowRect);
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.
*/
- (CPTrackingArea)initWithRect:(CGRect)aRect options:(CPTrackingAreaOptions)options owner:(id)owner userInfo:(CPDictionary)userInfo
{
if (owner === nil)
[CPException raise:CPInternalInconsistencyException reason:"No owner specified"];
if (options === 0)
[CPException raise:CPInternalInconsistencyException reason:"Invalid CPTrackingArea options"];
// Check options:
// - at least one of CPTrackingMouseEnteredAndExited, CPTrackingMouseMoved, CPTrackingCursorUpdate
// - exactly one of CPTrackingActiveWhenFirstResponder, CPTrackingActiveInKeyWindow, CPTrackingActiveInActiveApp, CPTrackingActiveAlways
// - no check on CPTrackingAssumeInside, CPTrackingInVisibleRect, CPTrackingEnableDuringMouseDrag
if (!((options & CPTrackingMouseEnteredAndExited) || (options & CPTrackingMouseMoved) || (options & CPTrackingCursorUpdate)))
[CPException raise:CPInternalInconsistencyException reason:"Invalid CPTrackingAreaOptions: must use at least one of [CPTrackingMouseEnteredAndExited | CPTrackingMouseMoved | CPTrackingCursorUpdate]"];
if ((((options & CPTrackingActiveWhenFirstResponder) > 0) + ((options & CPTrackingActiveInKeyWindow) > 0) + ((options & CPTrackingActiveInActiveApp) > 0) + ((options & CPTrackingActiveAlways) > 0)) !== 1)
[CPException raise:CPInternalInconsistencyException reason:"Tracking area options may only specify one of [CPTrackingActiveWhenFirstResponder | CPTrackingActiveInKeyWindow | CPTrackingActiveInActiveApp | CPTrackingActiveAlways]."];
if (self = [super init])
{
_viewRect = aRect;
_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;
}
#pragma mark -
#pragma mark Implementation
- (void)_updateWindowRect
{
[self _updateWindowRectWithReferencingSuperViewVisibleRect:nil];
}
/*!
To speed up things we allow the caller to pass the visible rect of the referencing view's superview.
This allows the visible rect calculations to be optimized when traveling down the view hierarchy.
*/
- (void)_updateWindowRectWithReferencingSuperViewVisibleRect:(CGRect)referencingSuperviewVisibleRect
{
_windowRect = [_referencingView convertRect:((_options & CPTrackingInVisibleRect) ? [_referencingView _visibleRectWithSuperviewVisibleRect:referencingSuperviewVisibleRect] : _viewRect) toView:[[_referencingView window] _windowView]];
}
@end
#pragma mark -
#pragma mark CPCoding
@implementation CPTrackingArea (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder
{
if (self = [super init])
{
_viewRect = [aCoder decodeObjectForKey:CPTrackingAreaViewRectKey];
_options = [aCoder decodeObjectForKey:CPTrackingAreaOptionsKey];
_owner = [aCoder decodeObjectForKey:CPTrackingAreaOwnerKey];
_userInfo = [aCoder decodeObjectForKey:CPTrackingAreaUserInfoKey];
_referencingView = [aCoder decodeObjectForKey:CPTrackingAreaReferencingViewKey];
_windowRect = [aCoder decodeObjectForKey:CPTrackingAreaWindowRect];
}
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[aCoder encodeObject:_viewRect forKey:CPTrackingAreaViewRectKey];
[aCoder encodeObject:_options forKey:CPTrackingAreaOptionsKey];
[aCoder encodeObject:_owner forKey:CPTrackingAreaOwnerKey];
[aCoder encodeObject:_userInfo forKey:CPTrackingAreaUserInfoKey];
[aCoder encodeObject:_referencingView forKey:CPTrackingAreaReferencingViewKey];
[aCoder encodeObject:_windowRect forKey:CPTrackingAreaWindowRect];
}
@end