mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 04:57:03 +00:00
981 lines
28 KiB
Plaintext
981 lines
28 KiB
Plaintext
/*
|
|
* CALayer.j
|
|
* AppKit
|
|
*
|
|
* Created by Francisco Tolmasky.
|
|
* Copyright 2008, 280 North, Inc.
|
|
*
|
|
* 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/CPObject.j>
|
|
|
|
import <AppKit/CABackingStore.j>
|
|
|
|
import <AppKit/CGContext.j>
|
|
import <AppKit/CGGeometry.j>
|
|
|
|
#include "../CoreGraphics/CGGeometry.h"
|
|
#include "../CoreGraphics/CGAffineTransform.j"
|
|
|
|
|
|
#define DOM(aLayer) aLayer._DOMElement
|
|
|
|
var CALayerGeometryBoundsMask = 1,
|
|
CALayerGeometryPositionMask = 2,
|
|
CALayerGeometryAnchorPointMask = 4,
|
|
CALayerGeometryAffineTransformMask = 8,
|
|
CALayerGeometryParentSublayerTransformMask = 16;
|
|
var USE_BUFFER = NO;
|
|
|
|
var CALayerFrameOriginUpdateMask = 1,
|
|
CALayerFrameSizeUpdateMask = 2,
|
|
CALayerZPositionUpdateMask = 4,
|
|
CALayerDisplayUpdateMask = 8,
|
|
CALayerCompositeUpdateMask = 16,
|
|
CALayerDOMUpdateMask = CALayerZPositionUpdateMask | CALayerFrameOriginUpdateMask | CALayerFrameSizeUpdateMask;
|
|
|
|
var CALayerRegisteredRunLoopUpdates = nil;
|
|
|
|
@implementation CALayer : CPObject
|
|
{
|
|
// Modifying the Layer Geometry
|
|
|
|
CGRect _frame;
|
|
CGRect _bounds;
|
|
CGPoint _position;
|
|
unsigned _zPosition;
|
|
CGPoint _anchorPoint;
|
|
|
|
CGAffineTransform _affineTransform;
|
|
CGAffineTransform _sublayerTransform;
|
|
CGAffineTransform _sublayerTransformForSublayers;
|
|
|
|
CGRect _backingStoreFrame;
|
|
CGRect _standardBackingStoreFrame;
|
|
|
|
BOOL _hasSublayerTransform;
|
|
BOOL _hasCustomBackingStoreFrame;
|
|
|
|
// Style Attributes
|
|
|
|
float _opacity;
|
|
BOOL _isHidden;
|
|
CPColor _backgroundColor;
|
|
|
|
// Managing Layer Hierarchy
|
|
|
|
CALayer _superlayer;
|
|
CPMutableArray _sublayers;
|
|
|
|
// Updating Layer Display
|
|
|
|
unsigned _runLoopUpdateMask;
|
|
BOOL _needsDisplayOnBoundsChange;
|
|
|
|
// Modifyin the Delegate
|
|
|
|
id _delegate;
|
|
|
|
BOOL _delegateRespondsToDisplayLayerSelector;
|
|
BOOL _delegateRespondsToDrawLayerInContextSelector;
|
|
|
|
// DOM Implementation
|
|
|
|
DOMElement _DOMElement;
|
|
DOMElement _DOMContentsElement;
|
|
id _contents;
|
|
CGContext _context;
|
|
CPView _owningView;
|
|
|
|
CGAffineTransform _transformToLayer;
|
|
CGAffineTransform _transformFromLayer;
|
|
}
|
|
|
|
+ (CALayer)layer
|
|
{
|
|
return [[[self class] alloc] init];
|
|
}
|
|
|
|
- (id)init
|
|
{
|
|
self = [super init];
|
|
|
|
if (self)
|
|
{
|
|
_frame = CGRectMakeZero();
|
|
|
|
_backingStoreFrame = CGRectMakeZero();
|
|
_standardBackingStoreFrame = CGRectMakeZero();
|
|
|
|
_bounds = CGRectMakeZero();
|
|
_position = CGPointMakeZero();
|
|
_zPosition = 0.0;
|
|
_anchorPoint = CGPointMake(0.5, 0.5);
|
|
_affineTransform = CGAffineTransformMakeIdentity();
|
|
_sublayerTransform = CGAffineTransformMakeIdentity();
|
|
|
|
_transformToLayer = CGAffineTransformMakeIdentity(); // FIXME? does it matter?
|
|
_transformFromLayer = CGAffineTransformMakeIdentity();
|
|
|
|
_opacity = 1.0;
|
|
_isHidden = NO;
|
|
_masksToBounds = NO;
|
|
|
|
_sublayers = [];
|
|
|
|
_DOMElement = document.createElement("div");
|
|
|
|
_DOMElement.style.overflow = "visible";
|
|
_DOMElement.style.position = "absolute";
|
|
_DOMElement.style.visibility = "visible";
|
|
_DOMElement.style.top = "0px";
|
|
_DOMElement.style.left = "0px";
|
|
_DOMElement.style.zIndex = 0;
|
|
_DOMElement.style.width = "0px";
|
|
_DOMElement.style.height = "0px";
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
// Modifying the Layer Geometry
|
|
|
|
- (void)setBounds:(CGRect)aBounds
|
|
{
|
|
if (CGRectEqualToRect(_bounds, aBounds))
|
|
return;
|
|
|
|
var oldOrigin = _bounds.origin;
|
|
|
|
_bounds = _CGRectMakeCopy(aBounds);
|
|
|
|
if (_hasSublayerTransform)
|
|
_CALayerUpdateSublayerTransformForSublayers(self);
|
|
|
|
// _hasSublayerTransform == true will handle this for us.
|
|
/*else if (!CGPointEqualToPoint(_bounds.origin, oldOrigin))
|
|
{
|
|
var index = _sublayers.length;
|
|
|
|
// FIXME: This should climb the layer tree down.
|
|
while (index--)
|
|
_CALayerRecalculateGeometry(_sublayers[index], CALayerGeometryPositionMask);
|
|
}*/
|
|
|
|
_CALayerRecalculateGeometry(self, CALayerGeometryBoundsMask);
|
|
}
|
|
|
|
- (CGRect)bounds
|
|
{
|
|
return _bounds;
|
|
}
|
|
|
|
- (void)setPosition:(CGPoint)aPosition
|
|
{
|
|
if (CGPointEqualToPoint(_position, aPosition))
|
|
return;
|
|
|
|
_position = _CGPointMakeCopy(aPosition);
|
|
|
|
_CALayerRecalculateGeometry(self, CALayerGeometryPositionMask);
|
|
}
|
|
|
|
- (CGPoint)position
|
|
{
|
|
return _position;
|
|
}
|
|
|
|
- (void)setZPosition:(int)aZPosition
|
|
{
|
|
if (_zPosition == aZPosition)
|
|
return;
|
|
|
|
_zPosition = aZPosition;
|
|
|
|
[self registerRunLoopUpdateWithMask:CALayerZPositionUpdateMask];
|
|
}
|
|
|
|
- (void)setAnchorPoint:(CGPoint)anAnchorPoint
|
|
{
|
|
anAnchorPoint = _CGPointMakeCopy(anAnchorPoint);
|
|
anAnchorPoint.x = MIN(1.0, MAX(0.0, anAnchorPoint.x));
|
|
anAnchorPoint.y = MIN(1.0, MAX(0.0, anAnchorPoint.y));
|
|
|
|
if (CGPointEqualToPoint(_anchorPoint, anAnchorPoint))
|
|
return;
|
|
|
|
_anchorPoint = anAnchorPoint;
|
|
|
|
if (_hasSublayerTransform)
|
|
_CALayerUpdateSublayerTransformForSublayers(self);
|
|
|
|
if (_owningView)
|
|
_position = CGPointMake(_CGRectGetWidth(_bounds) * _anchorPoint.x, _CGRectGetHeight(_bounds) * _anchorPoint.y);
|
|
|
|
_CALayerRecalculateGeometry(self, CALayerGeometryAnchorPointMask);
|
|
}
|
|
|
|
- (CGPoint)anchorPoint
|
|
{
|
|
return _anchorPoint;
|
|
}
|
|
|
|
- (void)setAffineTransform:(CGAffineTransform)anAffineTransform
|
|
{
|
|
if (CGAffineTransformEqualToTransform(_affineTransform, anAffineTransform))
|
|
return;
|
|
|
|
_affineTransform = _CGAffineTransformMakeCopy(anAffineTransform);
|
|
|
|
_CALayerRecalculateGeometry(self, CALayerGeometryAffineTransformMask);
|
|
}
|
|
|
|
- (CGAffineTransform)affineTransform
|
|
{
|
|
return _affineTransform;
|
|
}
|
|
|
|
- (void)setSublayerTransform:(CGAffineTransform)anAffineTransform
|
|
{
|
|
if (CGAffineTransformEqualToTransform(_sublayerTransform, anAffineTransform))
|
|
return;
|
|
|
|
var hadSublayerTransform = _hasSublayerTransform;
|
|
|
|
_sublayerTransform = _CGAffineTransformMakeCopy(anAffineTransform);
|
|
_hasSublayerTransform = !_CGAffineTransformIsIdentity(_sublayerTransform);
|
|
|
|
if (_hasSublayerTransform)
|
|
{
|
|
_CALayerUpdateSublayerTransformForSublayers(self);
|
|
|
|
var index = _sublayers.length;
|
|
|
|
// FIXME: This should climb the layer tree down.
|
|
while (index--)
|
|
_CALayerRecalculateGeometry(_sublayers[index], CALayerGeometryParentSublayerTransformMask);
|
|
}
|
|
}
|
|
|
|
- (CGAffineTransform)sublayerTransform
|
|
{
|
|
return _sublayerTransform;
|
|
}
|
|
|
|
- (CGAffineTransform)transformToLayer
|
|
{
|
|
return _transformToLayer;
|
|
}
|
|
|
|
- (void)setFrame:(CGRect)aFrame
|
|
{
|
|
alert("FIXME IMPLEMENT");
|
|
}
|
|
|
|
// The frame defines the bounding box of the layer: the smallest
|
|
// possible rectangle that could fit this layer after transform
|
|
// properties are applied in superlayer coordinates.
|
|
- (CGRect)frame
|
|
{
|
|
if (!_frame)
|
|
_frame = [self convertRect:_bounds toLayer:_superlayer];
|
|
|
|
return _frame;
|
|
}
|
|
|
|
// The Backing Store Frame specifies the frame of the actual backing
|
|
// store used to contain this layer. Naturally, by default it is the
|
|
// same as the frame, however, users can specify their own custom
|
|
// Backing Store Frame in order to speed up certain operations, such as
|
|
// live transformation.
|
|
- (CGRect)backingStoreFrame
|
|
{
|
|
return _backingStoreFrame;
|
|
}
|
|
|
|
- (void)setBackingStoreFrame:(CGRect)aFrame
|
|
{
|
|
_hasCustomBackingStoreFrame = (aFrame != nil);
|
|
|
|
if (aFrame == nil)
|
|
aFrame = CGRectMakeCopy(_standardBackingStoreFrame);
|
|
else
|
|
{
|
|
if (_superlayer)
|
|
{
|
|
aFrame = [_superlayer convertRect:aFrame toLayer:nil];
|
|
|
|
var bounds = [_superlayer bounds],
|
|
frame = [_superlayer convertRect:bounds toLayer:nil];
|
|
|
|
aFrame.origin.x -= _CGRectGetMinX(frame);
|
|
aFrame.origin.y -= _CGRectGetMinY(frame);
|
|
}
|
|
else
|
|
aFrame = CGRectMakeCopy(aFrame);
|
|
}
|
|
|
|
if (!CGPointEqualToPoint(_backingStoreFrame.origin, aFrame.origin))
|
|
[self registerRunLoopUpdateWithMask:CALayerFrameOriginUpdateMask];
|
|
|
|
if (!CGSizeEqualToSize(_backingStoreFrame.size, aFrame.size))
|
|
[self registerRunLoopUpdateWithMask:CALayerFrameSizeUpdateMask];
|
|
|
|
_backingStoreFrame = aFrame;
|
|
}
|
|
|
|
// Providing Layer Content
|
|
|
|
- (CGImage)contents
|
|
{
|
|
return _contents;
|
|
}
|
|
|
|
- (void)setContents:(CGImage)contents
|
|
{
|
|
if (_contents == contents)
|
|
return;
|
|
|
|
_contents = contents;
|
|
|
|
[self composite];
|
|
}
|
|
|
|
- (void)composite
|
|
{
|
|
if (USE_BUFFER && !_contents || !_context)
|
|
return;
|
|
|
|
CGContextClearRect(_context, _CGRectMake(0.0, 0.0, _CGRectGetWidth(_backingStoreFrame), _CGRectGetHeight(_backingStoreFrame)));
|
|
|
|
// Recomposite
|
|
var transform = _transformFromLayer;
|
|
|
|
if (_superlayer)
|
|
{
|
|
var superlayerTransform = _CALayerGetTransform(_superlayer, nil),
|
|
superlayerOrigin = CGPointApplyAffineTransform(_superlayer._bounds.origin, superlayerTransform);
|
|
|
|
transform = CGAffineTransformConcat(transform, superlayerTransform);
|
|
|
|
transform.tx -= superlayerOrigin.x;
|
|
transform.ty -= superlayerOrigin.y;
|
|
}
|
|
|
|
transform.tx -= _CGRectGetMinX(_backingStoreFrame);
|
|
transform.ty -= _CGRectGetMinY(_backingStoreFrame);
|
|
|
|
CGContextSaveGState(_context);
|
|
CGContextConcatCTM(_context, transform);//_transformFromView);
|
|
if (USE_BUFFER)
|
|
{
|
|
// CGContextDrawImage(_context, _bounds, _contents.context);
|
|
_context.drawImage(_contents.buffer, _CGRectGetMinX(_bounds), _CGRectGetMinY(_bounds));//, _CGRectGetWidth(_standardBackingStoreFrame), _CGRectGetHeight(_standardBackingStoreFrame));
|
|
}
|
|
else
|
|
[self drawInContext:_context];
|
|
CGContextRestoreGState(_context);
|
|
}
|
|
|
|
- (void)display
|
|
{
|
|
if (!_context)
|
|
{
|
|
_context = CGBitmapGraphicsContextCreate();
|
|
|
|
_DOMContentsElement = _context.DOMElement;
|
|
|
|
_DOMContentsElement.style.zIndex = -100;
|
|
|
|
_DOMContentsElement.style.overflow = "hidden";
|
|
_DOMContentsElement.style.position = "absolute";
|
|
_DOMContentsElement.style.visibility = "visible";
|
|
|
|
_DOMContentsElement.width = ROUND(_CGRectGetWidth(_backingStoreFrame));
|
|
_DOMContentsElement.height = ROUND(_CGRectGetHeight(_backingStoreFrame));
|
|
|
|
_DOMContentsElement.style.top = "0px";
|
|
_DOMContentsElement.style.left = "0px";
|
|
_DOMContentsElement.style.width = ROUND(_CGRectGetWidth(_backingStoreFrame)) + "px";
|
|
_DOMContentsElement.style.height = ROUND(_CGRectGetHeight(_backingStoreFrame)) + "px";
|
|
|
|
_DOMElement.appendChild(_DOMContentsElement);
|
|
}
|
|
|
|
if (USE_BUFFER)
|
|
{
|
|
if (_delegateRespondsToDisplayLayerSelector)
|
|
return [self displayInLayer:self];
|
|
|
|
if (_CGRectGetWidth(_backingStoreFrame) == 0.0 || _CGRectGetHeight(_backingStoreFrame) == 0.0)
|
|
return;
|
|
|
|
if (!_contents)
|
|
_contents = CABackingStoreCreate();
|
|
|
|
CABackingStoreSetSize(_contents, _bounds.size);
|
|
|
|
[self drawInContext:CABackingStoreGetContext(_contents)];
|
|
}
|
|
|
|
[self composite];
|
|
}
|
|
|
|
- (void)drawInContext:(CGContext)aContext
|
|
{ //if (!window.loop || window.nodisplay) CPLog.error("htiasd");
|
|
if (_backgroundColor)
|
|
{
|
|
CGContextSetFillColor(aContext, _backgroundColor);
|
|
CGContextFillRect(aContext, _bounds);
|
|
}
|
|
|
|
if (_delegateRespondsToDrawLayerInContextSelector)
|
|
[_delegate drawLayer:self inContext:aContext];
|
|
}
|
|
|
|
|
|
// Style Attributes
|
|
|
|
- (float)opacity
|
|
{
|
|
return _opacity;
|
|
}
|
|
|
|
- (void)setOpacity:(float)anOpacity
|
|
{
|
|
if (_opacity == anOpacity)
|
|
return;
|
|
|
|
_opacity = anOpacity;
|
|
|
|
_DOMElement.style.opacity = anOpacity;
|
|
_DOMElement.style.filter = "alpha(opacity=" + anOpacity * 100 + ")";
|
|
}
|
|
|
|
- (void)setHidden:(BOOL)isHidden
|
|
{
|
|
_isHidden = isHidden;
|
|
_DOMElement.style.display = isHidden ? "none" : "block";
|
|
}
|
|
|
|
- (BOOL)hidden
|
|
{
|
|
return _isHidden;
|
|
}
|
|
|
|
- (BOOL)isHidden
|
|
{
|
|
return _isHidden;
|
|
}
|
|
|
|
- (void)setMasksToBounds:(BOOL)masksToBounds
|
|
{
|
|
if (_masksToBounds == masksToBounds)
|
|
return;
|
|
|
|
_masksToBounds = masksToBounds;
|
|
_DOMElement.style.overflow = _masksToBounds ? "hidden" : "visible";
|
|
}
|
|
|
|
- (void)setBackgroundColor:(CPColor)aColor
|
|
{
|
|
_backgroundColor = aColor;
|
|
|
|
[self setNeedsDisplay];
|
|
}
|
|
|
|
- (CPColor)backgroundColor
|
|
{
|
|
return _backgroundColor;
|
|
}
|
|
|
|
// Managing Layer Hierarchy
|
|
|
|
- (CPArray)sublayers
|
|
{
|
|
return _sublayers;
|
|
}
|
|
|
|
- (CALayer)superlayer
|
|
{
|
|
return _superlayer;
|
|
}
|
|
|
|
#define ADJUST_CONTENTS_ZINDEX(aLayer)\
|
|
if (_DOMContentsElement && aLayer._zPosition > _DOMContentsElement.style.zIndex)\
|
|
_DOMContentsElement.style.zIndex -= 100.0;\
|
|
|
|
- (void)addSublayer:(CALayer)aLayer
|
|
{
|
|
[self insertSublayer:aLayer atIndex:_sublayers.length];
|
|
return;
|
|
ADJUST_CONTENTS_ZINDEX(aLayer);
|
|
|
|
[_sublayers addObject:aLayer];
|
|
_DOMElement.appendChild(DOM(aLayer));
|
|
}
|
|
|
|
- (void)removeFromSuperlayer
|
|
{
|
|
if (_owningView)
|
|
[_owningView setLayer:nil];
|
|
|
|
if (!_superlayer)
|
|
return;
|
|
|
|
_superlayer._DOMElement.removeChild(_DOMElement);
|
|
[_superlayer._sublayers removeObject:self];
|
|
|
|
_superlayer = nil;
|
|
}
|
|
|
|
- (void)insertSublayer:(CALayer)aLayer atIndex:(unsigned)anIndex
|
|
{
|
|
if (!aLayer)
|
|
return;
|
|
|
|
var superlayer = [aLayer superlayer];
|
|
|
|
if (superlayer == self)
|
|
{
|
|
var index = [_sublayers indexOfObjectIdenticalTo:aLayer];
|
|
|
|
if (index == anIndex)
|
|
return;
|
|
|
|
[_sublayers removeObjectAtIndex:index];
|
|
|
|
if (index < anIndex)
|
|
--anIndex;
|
|
}
|
|
else if (superlayer != nil)
|
|
[aLayer removeFromSuperlayer];
|
|
|
|
ADJUST_CONTENTS_ZINDEX(aLayer);
|
|
|
|
[_sublayers insertObject:aLayer atIndex:anIndex];
|
|
|
|
if (anIndex >= _sublayers.length - 1)
|
|
_DOMElement.appendChild(DOM(aLayer));
|
|
else
|
|
_DOMElement.insertBefore(DOM(aLayer), _sublayers[anIndex + 1]._DOMElement);
|
|
|
|
aLayer._superlayer = self;
|
|
|
|
if (self != superlayer)
|
|
_CALayerRecalculateGeometry(aLayer, 0xFFFFFFF);
|
|
}
|
|
|
|
- (void)insertSublayer:(CALayer)aLayer below:(CALayer)aSublayer
|
|
{
|
|
var index = aSublayer ? [_sublayers indexOfObjectIdenticalTo:aSublayer] : 0;
|
|
|
|
[self insertSublayer:aLayer atIndex:index == CPNotFound ? _sublayers.length : index];
|
|
}
|
|
|
|
- (void)insertSublayer:(CALayer)aLayer after:(CALayer)aSublayer
|
|
{
|
|
var index = aSublayer ? [_sublayers indexOfObjectIdenticalTo:aSublayer] : _sublayers.length;
|
|
|
|
[_sublayers insertObject:aLayer atIndex:index == CPNotFound ? _sublayers.length : index + 1];
|
|
}
|
|
|
|
- (void)replaceSublayer:(CALayer)aSublayer with:(CALayer)aLayer
|
|
{
|
|
if (aSublayer == aLayer)
|
|
return;
|
|
|
|
// FIXME: EXCEPTION
|
|
if (aSublayer._superlayer != self)
|
|
{
|
|
alert("EXCEPTION");
|
|
return;
|
|
}
|
|
|
|
ADJUST_CONTENTS_ZINDEX(aLayer);
|
|
|
|
[_sublayers replaceObjectAtIndex:[_sublayers indexOfObjectIdenticalTo:aSublayer] withObject:aLayer];
|
|
_DOMElement.replaceChild(DOM(aSublayer), DOM(aLayer));
|
|
}
|
|
|
|
// Updating Layer Display
|
|
|
|
+ (void)runLoopUpdateLayers
|
|
{if (window.oops) {alert(window.latest); objj_debug_print_backtrace();}
|
|
window.loop = true;
|
|
for (hash in CALayerRegisteredRunLoopUpdates)
|
|
{
|
|
var layer = CALayerRegisteredRunLoopUpdates[hash],
|
|
mask = layer._runLoopUpdateMask;
|
|
|
|
if (mask & CALayerDOMUpdateMask)
|
|
_CALayerUpdateDOM(layer, mask);
|
|
|
|
if (mask & CALayerDisplayUpdateMask)
|
|
[layer display];
|
|
|
|
else if (mask & CALayerFrameSizeUpdateMask || mask & CALayerCompositeUpdateMask)
|
|
[layer composite];
|
|
|
|
layer._runLoopUpdateMask = 0;
|
|
}
|
|
window.loop= false;
|
|
CALayerRegisteredRunLoopUpdates = nil;
|
|
}
|
|
|
|
- (void)registerRunLoopUpdateWithMask:(unsigned)anUpdateMask
|
|
{
|
|
if (CALayerRegisteredRunLoopUpdates == nil)
|
|
{
|
|
CALayerRegisteredRunLoopUpdates = {};
|
|
|
|
[[CPRunLoop currentRunLoop] performSelector:@selector(runLoopUpdateLayers)
|
|
target:CALayer argument:nil order:0 modes:[CPDefaultRunLoopMode]];
|
|
}
|
|
|
|
_runLoopUpdateMask |= anUpdateMask;
|
|
CALayerRegisteredRunLoopUpdates[[self hash]] = self;
|
|
}
|
|
|
|
- (void)setNeedsComposite
|
|
{
|
|
[self registerRunLoopUpdateWithMask:CALayerCompositeUpdateMask];
|
|
}
|
|
|
|
- (void)setNeedsDisplay
|
|
{
|
|
[self registerRunLoopUpdateWithMask:CALayerDisplayUpdateMask];
|
|
}
|
|
|
|
- (void)setNeedsDisplayOnBoundsChange:(BOOL)needsDisplayOnBoundsChange
|
|
{
|
|
_needsDisplayOnBoundsChange = needsDisplayOnBoundsChange;
|
|
}
|
|
|
|
- (BOOL)needsDisplayOnBoundsChange
|
|
{
|
|
return _needsDisplayOnBoundsChange;
|
|
}
|
|
|
|
- (void)setNeedsDisplayInRect:(CGRect)aRect
|
|
{
|
|
_dirtyRect = aRect;
|
|
[self display];
|
|
}
|
|
|
|
// Mapping Between Coordinate and Time Spaces
|
|
|
|
- (CGPoint)convertPoint:(CGPoint)aPoint fromLayer:(CALayer)aLayer
|
|
{
|
|
return CGPointApplyAffineTransform(aPoint, _CALayerGetTransform(aLayer, self));
|
|
}
|
|
|
|
- (CGPoint)convertPoint:(CGPoint)aPoint toLayer:(CALayer)aLayer
|
|
{
|
|
return CGPointApplyAffineTransform(aPoint, _CALayerGetTransform(self, aLayer));
|
|
}
|
|
|
|
- (CGRect)convertRect:(CGRect)aRect fromLayer:(CALayer)aLayer
|
|
{
|
|
return CGRectApplyAffineTransform(aRect, _CALayerGetTransform(aLayer, self));
|
|
}
|
|
|
|
- (CGRect)convertRect:(CGRect)aRect toLayer:(CALayer)aLayer
|
|
{
|
|
return CGRectApplyAffineTransform(aRect, _CALayerGetTransform(self, aLayer));
|
|
}
|
|
|
|
// Hit Testing
|
|
|
|
- (BOOL)containsPoint:(CGPoint)aPoint
|
|
{
|
|
return _CGRectContainsPoint(_bounds, aPoint);
|
|
}
|
|
|
|
- (CALayer)hitTest:(CGPoint)aPoint
|
|
{
|
|
if (_isHidden)
|
|
return nil;
|
|
|
|
var point = CGPointApplyAffineTransform(aPoint, _transformToLayer);
|
|
//alert(point.x + " " + point.y);
|
|
|
|
if (!_CGRectContainsPoint(_bounds, point))
|
|
return nil;
|
|
|
|
var layer = nil,
|
|
index = _sublayers.length;
|
|
|
|
// FIXME: this should take into account zPosition.
|
|
while (index--)
|
|
if (layer = [_sublayers[index] hitTest:point])
|
|
return layer;
|
|
|
|
return self;
|
|
}
|
|
|
|
// Modifying the Delegate
|
|
|
|
- (void)setDelegate:(id)aDelegate
|
|
{
|
|
if (_delegate == aDelegate)
|
|
return;
|
|
|
|
_delegate = aDelegate;
|
|
|
|
_delegateRespondsToDisplayLayerSelector = [_delegate respondsToSelector:@selector(displayLayer:)];
|
|
_delegateRespondsToDrawLayerInContextSelector = [_delegate respondsToSelector:@selector(drawLayer:inContext:)];
|
|
|
|
if (_delegateRespondsToDisplayLayerSelector || _delegateRespondsToDrawLayerInContextSelector)
|
|
[self setNeedsDisplay];
|
|
}
|
|
|
|
- (id)delegate
|
|
{
|
|
return _delegate;
|
|
}
|
|
|
|
- (void)_setOwningView:(CPView)anOwningView
|
|
{
|
|
_owningView = anOwningView;
|
|
|
|
if (_owningView)
|
|
{
|
|
_owningView = anOwningView;
|
|
|
|
_bounds.size = CGSizeMakeCopy([_owningView bounds].size);
|
|
_position = CGPointMake(_CGRectGetWidth(_bounds) * _anchorPoint.x, _CGRectGetHeight(_bounds) * _anchorPoint.y);
|
|
}
|
|
|
|
_CALayerRecalculateGeometry(self, CALayerGeometryPositionMask | CALayerGeometryBoundsMask);
|
|
}
|
|
|
|
- (void)_owningViewBoundsChanged
|
|
{
|
|
_bounds.size = CGSizeMakeCopy([_owningView bounds].size);
|
|
_position = CGPointMake(_CGRectGetWidth(_bounds) * _anchorPoint.x, _CGRectGetHeight(_bounds) * _anchorPoint.y);
|
|
|
|
_CALayerRecalculateGeometry(self, CALayerGeometryPositionMask | CALayerGeometryBoundsMask);
|
|
}
|
|
|
|
- (void)_update
|
|
{
|
|
window.loop = true;
|
|
|
|
var mask = _runLoopUpdateMask;
|
|
|
|
if (mask & CALayerDOMUpdateMask)
|
|
_CALayerUpdateDOM(self, mask);
|
|
|
|
if (mask & CALayerDisplayUpdateMask)
|
|
[self display];
|
|
|
|
else if (mask & CALayerFrameSizeUpdateMask || mask & CALayerCompositeUpdateMask)
|
|
[self composite];
|
|
|
|
_runLoopUpdateMask = 0;
|
|
|
|
window.loop = false;
|
|
}
|
|
|
|
@end
|
|
|
|
function _CALayerUpdateSublayerTransformForSublayers(aLayer)
|
|
{
|
|
var bounds = aLayer._bounds,
|
|
anchorPoint = aLayer._anchorPoint,
|
|
translateX = _CGRectGetWidth(bounds) * anchorPoint.x,
|
|
translateY = _CGRectGetHeight(bounds) * anchorPoint.y;
|
|
|
|
aLayer._sublayerTransformForSublayers = CGAffineTransformConcat(
|
|
CGAffineTransformMakeTranslation(-translateX, -translateY),
|
|
CGAffineTransformConcat(aLayer._sublayerTransform,
|
|
CGAffineTransformMakeTranslation(translateX, translateY)));
|
|
}
|
|
|
|
function _CALayerUpdateDOM(aLayer, aMask)
|
|
{
|
|
var DOMElementStyle = aLayer._DOMElement.style;
|
|
|
|
if (aMask & CALayerZPositionUpdateMask)
|
|
DOMElementStyle.zIndex = aLayer._zPosition;
|
|
|
|
var frame = aLayer._backingStoreFrame;
|
|
|
|
if (aMask & CALayerFrameOriginUpdateMask)
|
|
{
|
|
DOMElementStyle.top = ROUND(_CGRectGetMinY(frame)) + "px";
|
|
DOMElementStyle.left = ROUND(_CGRectGetMinX(frame)) + "px";
|
|
}
|
|
|
|
if (aMask & CALayerFrameSizeUpdateMask)
|
|
{
|
|
var width = MAX(0.0, ROUND(_CGRectGetWidth(frame))),
|
|
height = MAX(0.0, ROUND(_CGRectGetHeight(frame))),
|
|
DOMContentsElement = aLayer._DOMContentsElement;
|
|
|
|
DOMElementStyle.width = width + "px";
|
|
DOMElementStyle.height = height + "px";
|
|
|
|
if (DOMContentsElement)
|
|
{
|
|
DOMContentsElement.width = width;
|
|
DOMContentsElement.height = height;
|
|
DOMContentsElement.style.width = width + "px";
|
|
DOMContentsElement.style.height = height + "px";
|
|
}
|
|
}
|
|
}
|
|
|
|
function _CALayerRecalculateGeometry(aLayer, aGeometryChange)
|
|
{
|
|
var bounds = aLayer._bounds,
|
|
superlayer = aLayer._superlayer,
|
|
width = _CGRectGetWidth(bounds),
|
|
height = _CGRectGetHeight(bounds),
|
|
position = aLayer._position,
|
|
anchorPoint = aLayer._anchorPoint,
|
|
affineTransform = aLayer._affineTransform,
|
|
backingStoreFrameSize = _CGSizeMakeCopy(aLayer._backingStoreFrame),
|
|
hasCustomBackingStoreFrame = aLayer._hasCustomBackingStoreFrame;
|
|
|
|
// Go to anchor, transform, go back to bounds.
|
|
aLayer._transformFromLayer = CGAffineTransformConcat(
|
|
CGAffineTransformMakeTranslation(-width * anchorPoint.x - _CGRectGetMinX(aLayer._bounds), -height * anchorPoint.y - _CGRectGetMinY(aLayer._bounds)),
|
|
CGAffineTransformConcat(affineTransform,
|
|
CGAffineTransformMakeTranslation(position.x, position.y)));
|
|
|
|
if (superlayer && superlayer._hasSublayerTransform)
|
|
{
|
|
// aLayer._transformFromLayer = CGAffineTransformConcat(aLayer._transformFromLayer, superlayer._sublayerTransformForSublayers);
|
|
_CGAffineTransformConcatTo(aLayer._transformFromLayer, superlayer._sublayerTransformForSublayers, aLayer._transformFromLayer);
|
|
}
|
|
|
|
aLayer._transformToLayer = CGAffineTransformInvert(aLayer._transformFromLayer);
|
|
|
|
//aLayer._transformFromLayer.tx = ROUND(aLayer._transformFromLayer.tx);
|
|
//aLayer._transformFromLayer.ty = ROUND(aLayer._transformFromLayer.ty);
|
|
|
|
aLayer._frame = nil;
|
|
aLayer._standardBackingStoreFrame = [aLayer convertRect:bounds toLayer:nil];
|
|
|
|
if (superlayer)
|
|
{
|
|
var bounds = [superlayer bounds],
|
|
frame = [superlayer convertRect:bounds toLayer:nil];
|
|
|
|
aLayer._standardBackingStoreFrame.origin.x -= _CGRectGetMinX(frame);
|
|
aLayer._standardBackingStoreFrame.origin.y -= _CGRectGetMinY(frame);
|
|
}
|
|
|
|
// We used to use CGRectIntegral here, but what we actually want, is the largest integral
|
|
// rect that would ever contain this box, since for any width/height, there are 2 (4)
|
|
// possible integral rects for it depending on it's position. It's OK that this is sometimes
|
|
// bigger than the "optimal" bounding integral rect since that doesn't change drawing.
|
|
|
|
var origin = aLayer._standardBackingStoreFrame.origin,
|
|
size = aLayer._standardBackingStoreFrame.size;
|
|
|
|
origin.x = FLOOR(origin.x);
|
|
origin.y = FLOOR(origin.y);
|
|
size.width = CEIL(size.width) + 1.0;
|
|
size.height = CEIL(size.height) + 1.0;
|
|
|
|
// FIXME: This avoids the central issue that a position change is sometimes a display and sometimes
|
|
// a div move, and sometimes both.
|
|
|
|
// Only use this frame if we don't currently have a custom backing store frame.
|
|
if (!hasCustomBackingStoreFrame)
|
|
{
|
|
var backingStoreFrame = CGRectMakeCopy(aLayer._standardBackingStoreFrame);
|
|
|
|
// These values get rounded in the DOM, so don't both updating them if they're
|
|
// not going to be different after rounding.
|
|
if (ROUND(_CGRectGetMinX(backingStoreFrame)) != ROUND(_CGRectGetMinX(aLayer._backingStoreFrame)) ||
|
|
ROUND(_CGRectGetMinY(backingStoreFrame)) != ROUND(_CGRectGetMinY(aLayer._backingStoreFrame)))
|
|
[aLayer registerRunLoopUpdateWithMask:CALayerFrameOriginUpdateMask];
|
|
|
|
// Any change in size due to a geometry change is purely due to rounding error.
|
|
if ((_CGRectGetWidth(backingStoreFrame) != ROUND(_CGRectGetWidth(aLayer._backingStoreFrame)) ||
|
|
_CGRectGetHeight(backingStoreFrame) != ROUND(_CGRectGetHeight(aLayer._backingStoreFrame))))
|
|
[aLayer registerRunLoopUpdateWithMask:CALayerFrameSizeUpdateMask];
|
|
|
|
aLayer._backingStoreFrame = backingStoreFrame;
|
|
}
|
|
|
|
if (aGeometryChange & CALayerGeometryBoundsMask && aLayer._needsDisplayOnBoundsChange)
|
|
[aLayer setNeedsDisplay];
|
|
// We need to recomposite if we have a custom backing store frame, OR
|
|
// If the change is not solely composed of position and anchor points changes.
|
|
// Anchor point and position changes simply move the object, requiring
|
|
// no re-rendering.
|
|
else if (hasCustomBackingStoreFrame || (aGeometryChange & ~(CALayerGeometryPositionMask | CALayerGeometryAnchorPointMask)))
|
|
[aLayer setNeedsComposite];
|
|
|
|
var sublayers = aLayer._sublayers,
|
|
index = 0,
|
|
count = sublayers.length;
|
|
|
|
for (; index < count; ++index)
|
|
_CALayerRecalculateGeometry(sublayers[index], aGeometryChange);
|
|
}
|
|
|
|
function _CALayerGetTransform(fromLayer, toLayer)
|
|
{
|
|
var transform = CGAffineTransformMakeIdentity();
|
|
|
|
if (fromLayer)
|
|
{
|
|
var layer = fromLayer;
|
|
|
|
// If we have a fromLayer, "climb up" the layer tree until
|
|
// we hit the root node or we hit the toLayer.
|
|
while (layer && layer != toLayer)
|
|
{
|
|
var transformFromLayer = layer._transformFromLayer;
|
|
|
|
//transform = CGAffineTransformConcat(transform, layer._transformFromLayer);
|
|
_CGAffineTransformConcatTo(transform, transformFromLayer, transform);
|
|
|
|
layer = layer._superlayer;
|
|
}
|
|
|
|
// If we hit toLayer, then we're done.
|
|
if (layer == toLayer)
|
|
return transform;
|
|
}
|
|
|
|
var layers = [],
|
|
layer = toLayer;
|
|
|
|
while (layer)
|
|
{
|
|
layers.push(layer);
|
|
layer = layer._superlayer;
|
|
}
|
|
|
|
var index = layers.length;
|
|
|
|
while (index--)
|
|
{
|
|
var transformToLayer = layers[index]._transformToLayer;
|
|
|
|
_CGAffineTransformConcatTo(transform, transformToLayer, transform);
|
|
}
|
|
|
|
return transform;
|
|
}
|