From be08bed01e02b71752d07313509bf4a5bc018061 Mon Sep 17 00:00:00 2001 From: David Richardson Date: Thu, 27 Aug 2026 11:19:56 -0600 Subject: [PATCH 1/4] Revert "Merge pull request #3256 from enquora/modernize-cgcontext-canvas" This reverts commit e107e2d8046ba84e0af7cc03372edee4af4f710d, reversing changes made to 10351c3d4a898b4d6a0f0b74e4f4c616847b66bb. --- AppKit/CoreGraphics/CGContextCanvas.j | 665 +++++++++++++++----------- 1 file changed, 381 insertions(+), 284 deletions(-) diff --git a/AppKit/CoreGraphics/CGContextCanvas.j b/AppKit/CoreGraphics/CGContextCanvas.j index 1b629af98..d8d13e1f7 100644 --- a/AppKit/CoreGraphics/CGContextCanvas.j +++ b/AppKit/CoreGraphics/CGContextCanvas.j @@ -3,7 +3,6 @@ * AppKit * * Created by Francisco Tolmasky. - * Modified by David Richardson. * Copyright 2008, 280 North, Inc. * * This library is free software; you can redistribute it and/or @@ -21,518 +20,616 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -// MODIFICATION: Converted 'var' to 'const' for immutable lookup tables. -const CANVAS_LINECAP_TABLE = [ "butt", "round", "square" ]; -const CANVAS_LINEJOIN_TABLE = [ "miter", "round", "bevel" ]; -const CANVAS_COMPOSITE_TABLE = [ "source-over", "source-over", "source-over", "source-over", "darker", - "lighter", "source-over", "source-over", "source-over", "source-over", - "source-over", "source-over", "source-over", "source-over", "source-over", - "source-over", "source-over", - "copy", "source-in", "source-out", "source-atop", - "destination-over", "destination-in", "destination-out", "destination-atop", - "xor", "source-over", "source-over" ]; +var CANVAS_LINECAP_TABLE = [ "butt", "round", "square" ], + CANVAS_LINEJOIN_TABLE = [ "miter", "round", "bevel" ], + CANVAS_COMPOSITE_TABLE = [ "source-over", "source-over", "source-over", "source-over", "darker", + "lighter", "source-over", "source-over", "source-over", "source-over", + "source-over", "source-over", "source-over", "source-over", "source-over", + "source-over", "source-over", + "copy", "source-in", "source-out", "source-atop", + "destination-over", "destination-in", "destination-out", "destination-atop", + "xor", "source-over", "source-over" ]; -// MODIFICATION: Removed all #define macros. Canvas methods are now invoked directly within the target functions -// to eliminate the preprocessor pass and simplify AST generation. +#define _CGContextAddArcCanvas(aContext, x, y, radius, startAngle, endAngle, anticlockwise) aContext.arc(x, y, radius, startAngle, endAngle, anticlockwise) +#define _CGContextAddArcToPointCanvas(aContext, x1, y1, x2, y2, radius) aContext.arcTo(x1, y1, x2, y2, radius) +#define _CGContextAddCurveToPointCanvas(aContext, cp1x, cp1y, cp2x, cp2y, x, y) aContext.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) +#define _CGContextAddQuadCurveToPointCanvas(aContext, cpx, cpy, x, y) aContext.quadraticCurveTo(cpx, cpy, x, y) +#define _CGContextAddLineToPointCanvas(aContext, x, y) aContext.lineTo(x, y) +#define _CGContextClosePathCanvas(aContext) aContext.closePath() +#define _CGContextMoveToPointCanvas(aContext, x, y) aContext.moveTo(x, y) + +#define _CGContextAddRectCanvas(aContext, aRect) aContext.rect(CGRectGetMinX(aRect), CGRectGetMinY(aRect), CGRectGetWidth(aRect), CGRectGetHeight(aRect)) +#define _CGContextBeginPathCanvas(aContext) aContext.beginPath() +#define _CGContextFillRectCanvas(aContext, aRect) aContext.fillRect(CGRectGetMinX(aRect), CGRectGetMinY(aRect), CGRectGetWidth(aRect), CGRectGetHeight(aRect)) +#define _CGContextClipCanvas(aContext) aContext.clip() // In Cocoa, all primitives excepts rects and arcs cannot be added to the context's path // until a move to point has been done, because an empty path has no current point. -// MODIFICATION: Converted to const function expression and applied Allman formatting. -const hasPath = function(aContext, methodName) +var hasPath = function(aContext, methodName) { - if (!aContext.hasPath) - { - CPLog.error(methodName + ": no current point"); - } + if (!aContext.hasPath) + CPLog.error(methodName + ": no current point"); - return aContext.hasPath; -}; + return aContext.hasPath; +} function CGContextSaveGState(aContext) { - aContext.save(); + aContext.save(); } function CGContextRestoreGState(aContext) { - aContext.restore(); + aContext.restore(); } function CGContextSetLineCap(aContext, aLineCap) { - aContext.lineCap = CANVAS_LINECAP_TABLE[aLineCap]; + aContext.lineCap = CANVAS_LINECAP_TABLE[aLineCap]; } function CGContextSetLineDash(aContext, aPhase, someDashes) { - // MODIFICATION: Removed obsolete vendor prefixes (webkitLineDash, mozDash). - if (aContext.setLineDash) - { - aContext.setLineDash(someDashes); - aContext.lineDashOffset = aPhase; - } - else if (someDashes) - { - CPLog.warn("CGContextSetLineDash not implemented in this environment."); - } + if (aContext.setLineDash) + { + aContext.setLineDash(someDashes); + aContext.lineDashOffset = aPhase; + } + else if (typeof aContext['webkitLineDash'] !== 'undefined') + { + aContext.webkitLineDash = someDashes; + aContext.webkitLineDashOffset = aPhase; + } + else if (typeof aContext['mozDash'] !== 'undefined') + { + aContext.mozDash = someDashes; + aContext.mozDashOffset = aPhase; + } + else if (someDashes) + { + CPLog.warn("CGContextSetLineDash not implemented in this environment.") + } } function CGContextSetLineJoin(aContext, aLineJoin) { - aContext.lineJoin = CANVAS_LINEJOIN_TABLE[aLineJoin]; + aContext.lineJoin = CANVAS_LINEJOIN_TABLE[aLineJoin]; } function CGContextSetLineWidth(aContext, aLineWidth) { - aContext.lineWidth = aLineWidth; + aContext.lineWidth = aLineWidth; } function CGContextSetMiterLimit(aContext, aMiterLimit) { - aContext.miterLimit = aMiterLimit; + aContext.miterLimit = aMiterLimit; } function CGContextSetBlendMode(aContext, aBlendMode) { - aContext.globalCompositeOperation = CANVAS_COMPOSITE_TABLE[aBlendMode]; + aContext.globalCompositeOperation = CANVAS_COMPOSITE_TABLE[aBlendMode]; } function CGContextAddArc(aContext, x, y, radius, startAngle, endAngle, clockwise) { - // Despite the documentation saying otherwise, the last parameter is anti-clockwise not clockwise. - // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes#Arcs - // MODIFICATION: Direct invocation replacing _CGContextAddArcCanvas macro. - aContext.arc(x, y, radius, startAngle, endAngle, !clockwise); + // Despite the documentation saying otherwise, the last parameter is anti-clockwise not clockwise. + // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes#Arcs + _CGContextAddArcCanvas(aContext, x, y, radius, startAngle, endAngle, !clockwise); - // AddArc implicitly starts a path - aContext.hasPath = YES; + // AddArc implicitly starts a path + aContext.hasPath = YES; } function CGContextAddArcToPoint(aContext, x1, y1, x2, y2, radius) { - if (!hasPath(aContext, "CGContextAddArcToPoint")) - { - return; - } + if (!hasPath(aContext, "CGContextAddArcToPoint")) + return; - // MODIFICATION: Direct invocation replacing _CGContextAddArcToPointCanvas macro. - aContext.arcTo(x1, y1, x2, y2, radius); + _CGContextAddArcToPointCanvas(aContext, x1, y1, x2, y2, radius); } function CGContextAddCurveToPoint(aContext, cp1x, cp1y, cp2x, cp2y, x, y) { - if (!hasPath(aContext, "CGContextAddCurveToPoint")) - { - return; - } + if (!hasPath(aContext, "CGContextAddCurveToPoint")) + return; - // MODIFICATION: Direct invocation replacing _CGContextAddCurveToPointCanvas macro. - aContext.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y); + _CGContextAddCurveToPointCanvas(aContext, cp1x, cp1y, cp2x, cp2y, x, y); } function CGContextAddLines(aContext, points, count) { - // implementation mirrors that of CGPathAddLines() - if (count == null) - { - count = points.length; - } + // implementation mirrors that of CGPathAddLines() + if (count == null) + count = points.length; - if (count < 1) - { - return; - } + if (count < 1) + return; - // MODIFICATION: Direct invocations replacing macros. - aContext.moveTo(points[0].x, points[0].y); + _CGContextMoveToPointCanvas(aContext, points[0].x, points[0].y); - // MODIFICATION: Replaced 'var' with 'let'. - for (let i = 1; i < count; ++i) - { - aContext.lineTo(points[i].x, points[i].y); - } + for (var i = 1; i < count; ++i) + _CGContextAddLineToPointCanvas(aContext, points[i].x, points[i].y); - aContext.hasPath = YES; + aContext.hasPath = YES; } function CGContextAddLineToPoint(aContext, x, y) { - if (!hasPath(aContext, "CGContextAddLineToPoint")) - { - return; - } + if (!hasPath(aContext, "CGContextAddLineToPoint")) + return; - // MODIFICATION: Direct invocation replacing macro. - aContext.lineTo(x, y); + _CGContextAddLineToPointCanvas(aContext, x, y); } function CGContextAddPath(aContext, aPath) { - if (!aContext || CGPathIsEmpty(aPath)) - { - return; - } + if (!aContext || CGPathIsEmpty(aPath)) + return; - // If the context does not have a path, explicitly begin one - if (!aContext.hasPath) - { - aContext.beginPath(); - } + // If the context does not have a path, explicitly begin one + if (!aContext.hasPath) + _CGContextBeginPathCanvas(aContext); - // We must implicitly move to the start of the path - aContext.moveTo(aPath.start.x, aPath.start.y); + // We must implicitly move to the start of the path + _CGContextMoveToPointCanvas(aContext, aPath.start.x, aPath.start.y); - // MODIFICATION: Replaced 'var' with 'const'/'let'. - const elements = aPath.elements, - count = aPath.count; + var elements = aPath.elements, + i = 0, + count = aPath.count; - for (let i = 0; i < count; ++i) - { - const element = elements[i], - type = element.type; + for (; i < count; ++i) + { + var element = elements[i], + type = element.type; - switch (type) - { - case kCGPathElementMoveToPoint: - aContext.moveTo(element.x, element.y); - break; + switch (type) + { + case kCGPathElementMoveToPoint: + _CGContextMoveToPointCanvas(aContext, element.x, element.y); + break; - case kCGPathElementAddLineToPoint: - aContext.lineTo(element.x, element.y); - break; + case kCGPathElementAddLineToPoint: + _CGContextAddLineToPointCanvas(aContext, element.x, element.y); + break; - case kCGPathElementAddQuadCurveToPoint: - aContext.quadraticCurveTo(element.cpx, element.cpy, element.x, element.y); - break; + case kCGPathElementAddQuadCurveToPoint: + _CGContextAddQuadCurveToPointCanvas(aContext, element.cpx, element.cpy, element.x, element.y); + break; - case kCGPathElementAddCurveToPoint: - aContext.bezierCurveTo(element.cp1x, element.cp1y, element.cp2x, element.cp2y, element.x, element.y); - break; + case kCGPathElementAddCurveToPoint: + _CGContextAddCurveToPointCanvas(aContext, element.cp1x, element.cp1y, element.cp2x, element.cp2y, element.x, element.y); + break; - case kCGPathElementCloseSubpath: - aContext.closePath(); - break; + case kCGPathElementCloseSubpath: + _CGContextClosePathCanvas(aContext); + break; - case kCGPathElementAddArc: - aContext.arc(element.x, element.y, element.radius, element.startAngle, element.endAngle, element.clockwise); - break; + case kCGPathElementAddArc: + _CGContextAddArcCanvas(aContext, element.x, element.y, element.radius, element.startAngle, element.endAngle, element.clockwise); + break; - case kCGPathElementAddArcToPoint: - aContext.arcTo(element.p1x, element.p1y, element.p2x, element.p2y, element.radius); - break; - } - } + case kCGPathElementAddArcToPoint: + _CGContextAddArcToPointCanvas(aContext, element.p1x, element.p1y, element.p2x, element.p2y, element.radius); + break; + } + } - aContext.hasPath = YES; + aContext.hasPath = YES; } function CGContextAddRect(aContext, aRect) { - // MODIFICATION: Direct invocation replacing _CGContextAddRectCanvas macro. - aContext.rect(CGRectGetMinX(aRect), CGRectGetMinY(aRect), CGRectGetWidth(aRect), CGRectGetHeight(aRect)); - aContext.hasPath = YES; + _CGContextAddRectCanvas(aContext, aRect); + aContext.hasPath = YES; } function CGContextAddQuadCurveToPoint(aContext, cpx, cpy, x, y) { - if (!hasPath(aContext, "CGContextAddQuadCurveToPoint")) - { - return; - } + if (!hasPath(aContext, "CGContextAddQuadCurveToPoint")) + return; - // MODIFICATION: Direct invocation replacing macro. - aContext.quadraticCurveTo(cpx, cpy, x, y); + _CGContextAddQuadCurveToPointCanvas(aContext, cpx, cpy, x, y); } function CGContextAddRects(aContext, rects, count) { - if (count == null) - { - count = rects.length; - } + if (count == null) + count = rects.length; - for (let i = 0; i < count; ++i) - { - const rect = rects[i]; - aContext.rect(CGRectGetMinX(rect), CGRectGetMinY(rect), CGRectGetWidth(rect), CGRectGetHeight(rect)); - } + for (var i = 0; i < count; ++i) + { + var rect = rects[i]; + _CGContextAddRectCanvas(aContext, rect); + } - aContext.hasPath = YES; + aContext.hasPath = YES; } function CGContextBeginPath(aContext) { - aContext.beginPath(); - aContext.hasPath = NO; + _CGContextBeginPathCanvas(aContext); + aContext.hasPath = NO; } function CGContextClosePath(aContext) { - aContext.closePath(); + _CGContextClosePathCanvas(aContext); } function CGContextIsPathEmpty(aContext) { - return !aContext.hasPath; + return !aContext.hasPath; } function CGContextMoveToPoint(aContext, x, y) { - aContext.moveTo(x, y); - aContext.hasPath = YES; + _CGContextMoveToPointCanvas(aContext, x, y); + aContext.hasPath = YES; } function CGContextClearRect(aContext, aRect) { - aContext.clearRect(CGRectGetMinX(aRect), CGRectGetMinY(aRect), CGRectGetWidth(aRect), CGRectGetHeight(aRect)); - aContext.hasPath = NO; + aContext.clearRect(CGRectGetMinX(aRect), CGRectGetMinY(aRect), CGRectGetWidth(aRect), CGRectGetHeight(aRect)); + aContext.hasPath = NO; } function CGContextDrawPath(aContext, aMode) { - if (!aContext.hasPath) - { - return; - } + if (!aContext.hasPath) + return; - if (aMode === kCGPathFill || aMode === kCGPathFillStroke) - { - aContext.fill(); - } - else if (aMode === kCGPathStroke || aMode === kCGPathFillStroke || aMode === kCGPathEOFillStroke) - { - aContext.stroke(); - } - else if (aMode === kCGPathEOFill || aMode === kCGPathEOFillStroke) - { - CPLog.warn("Unimplemented fill mode in CGContextDrawPath: %d", aMode); - } + if (aMode === kCGPathFill || aMode === kCGPathFillStroke) + aContext.fill(); + else if (aMode === kCGPathStroke || aMode === kCGPathFillStroke || aMode === kCGPathEOFillStroke) + aContext.stroke(); + else if (aMode === kCGPathEOFill || aMode === kCGPathEOFillStroke) + CPLog.warn("Unimplemented fill mode in CGContextDrawPath: %d", aMode); - aContext.hasPath = NO; + aContext.hasPath = NO; } function CGContextFillRect(aContext, aRect) { - // MODIFICATION: Direct invocation replacing macro. - aContext.fillRect(CGRectGetMinX(aRect), CGRectGetMinY(aRect), CGRectGetWidth(aRect), CGRectGetHeight(aRect)); - aContext.hasPath = NO; + _CGContextFillRectCanvas(aContext, aRect); + aContext.hasPath = NO; } function CGContextFillRects(aContext, rects, count) { - if (count == null) - { - count = rects.length; - } + if (count == null) + count = rects.length; - for (let i = 0; i < count; ++i) - { - const rect = rects[i]; - aContext.fillRect(CGRectGetMinX(rect), CGRectGetMinY(rect), CGRectGetWidth(rect), CGRectGetHeight(rect)); - } + for (var i = 0; i < count; ++i) + { + var rect = rects[i]; + _CGContextFillRectCanvas(aContext, rect); + } - aContext.hasPath = NO; + aContext.hasPath = NO; } function CGContextStrokeRect(aContext, aRect) { - aContext.strokeRect(CGRectGetMinX(aRect), CGRectGetMinY(aRect), CGRectGetWidth(aRect), CGRectGetHeight(aRect)); - aContext.hasPath = NO; + aContext.strokeRect(CGRectGetMinX(aRect), CGRectGetMinY(aRect), CGRectGetWidth(aRect), CGRectGetHeight(aRect)); + aContext.hasPath = NO; } function CGContextClip(aContext) { - aContext.clip(); - aContext.hasPath = NO; + _CGContextClipCanvas(aContext); + aContext.hasPath = NO; } function CGContextClipToRect(aContext, aRect) { - aContext.beginPath(); - aContext.rect(CGRectGetMinX(aRect), CGRectGetMinY(aRect), CGRectGetWidth(aRect), CGRectGetHeight(aRect)); - aContext.closePath(); + _CGContextBeginPathCanvas(aContext); + _CGContextAddRectCanvas(aContext, aRect); + _CGContextClosePathCanvas(aContext); - aContext.clip(); - aContext.hasPath = NO; + _CGContextClipCanvas(aContext); + aContext.hasPath = NO; } function CGContextClipToRects(aContext, rects, count) { - if (count == null) - { - count = rects.length; - } + if (count == null) + count = rects.length; - aContext.beginPath(); - CGContextAddRects(aContext, rects, count); - aContext.clip(); - aContext.hasPath = NO; + _CGContextBeginPathCanvas(aContext); + CGContextAddRects(aContext, rects, count); + _CGContextClipCanvas(aContext); + aContext.hasPath = NO; } function CGContextSetAlpha(aContext, anAlpha) { - aContext.globalAlpha = anAlpha; + aContext.globalAlpha = anAlpha; } function CGContextSetFillColor(aContext, aColor) { - const patternImage = [aColor patternImage]; + var patternImage = [aColor patternImage]; - if ([patternImage isSingleImage]) - { - const pattern = aContext.createPattern([patternImage image], "repeat"); - aContext.fillStyle = pattern; - } - else - { - aContext.fillStyle = [aColor cssString]; - } + if ([patternImage isSingleImage]) + { + var pattern = aContext.createPattern([patternImage image], "repeat"); + + aContext.fillStyle = pattern; + } + else + aContext.fillStyle = [aColor cssString]; } /*! - Creates a context into which you can render a fill pattern - of the given size. Once the pattern is rendered, you can - set the fill or stroke pattern to the rendered pattern - with CGContextSetFillPattern or CGContextSetStrokePattern. - */ + Creates a context into which you can render a fill pattern + of the given size. Once the pattern is rendered, you can + set the fill or stroke pattern to the rendered pattern + with CGContextSetFillPattern or CGContextSetStrokePattern. +*/ function CGContextCreatePatternContext(aContext, aSize) { - const pattern = document.createElement("canvas"); + var pattern = document.createElement("canvas"); - pattern.width = aSize.width; - pattern.height = aSize.height; + pattern.width = aSize.width; + pattern.height = aSize.height; - return pattern.getContext("2d"); + return pattern.getContext("2d"); } /*! - Sets the fill pattern for aContext to the rendered pattern context - returned by CGContextCreatePatternContext. - */ + Sets the fill pattern for aContext to the rendered pattern context + returned by CGContextCreatePatternContext. +*/ function CGContextSetFillPattern(aContext, aPatternContext) { - const pattern = aContext.createPattern(aPatternContext.canvas, "repeat"); - aContext.fillStyle = pattern; + var pattern = aContext.createPattern(aPatternContext.canvas, "repeat"); + aContext.fillStyle = pattern; } /*! - Sets the stroke pattern for aContext to the rendered pattern context - returned by CGContextCreatePatternContext. - */ + Sets the stroke pattern for aContext to the rendered pattern context + returned by CGContextCreatePatternContext. +*/ function CGContextSetStrokePattern(aContext, aPatternContext) { - const pattern = aContext.createPattern(aPatternContext.canvas, "repeat"); - aContext.strokeStyle = pattern; + var pattern = aContext.createPattern(aPatternContext.canvas, "repeat"); + aContext.strokeStyle = pattern; } function CGContextSetStrokeColor(aContext, aColor) { - const patternImage = [aColor patternImage]; + var patternImage = [aColor patternImage]; - if ([patternImage isSingleImage]) - { - const pattern = aContext.createPattern([patternImage image], "repeat"); - aContext.strokeStyle = pattern; - } - else - { - aContext.strokeStyle = [aColor cssString]; - } + if ([patternImage isSingleImage]) + { + var pattern = aContext.createPattern([patternImage image], "repeat"); + + aContext.strokeStyle = pattern; + } + else + aContext.strokeStyle = [aColor cssString]; } function CGContextSetShadow(aContext, aSize, aBlur) { - aContext.shadowOffsetX = aSize.width; - aContext.shadowOffsetY = aSize.height; - aContext.shadowBlur = aBlur; - aContext.shadowColor = [[CPColor shadowColor] cssString]; + aContext.shadowOffsetX = aSize.width; + aContext.shadowOffsetY = aSize.height; + aContext.shadowBlur = aBlur; + aContext.shadowColor = [[CPColor shadowColor] cssString]; } function CGContextSetShadowWithColor(aContext, aSize, aBlur, aColor) { - aContext.shadowOffsetX = aSize.width; - aContext.shadowOffsetY = aSize.height; - aContext.shadowBlur = aBlur; - aContext.shadowColor = [aColor cssString]; + aContext.shadowOffsetX = aSize.width; + aContext.shadowOffsetY = aSize.height; + aContext.shadowBlur = aBlur; + aContext.shadowColor = [aColor cssString]; } function CGContextRotateCTM(aContext, anAngle) { - aContext.rotate(anAngle); + aContext.rotate(anAngle); } function CGContextScaleCTM(aContext, sx, sy) { - aContext.scale(sx, sy); + aContext.scale(sx, sy); } function CGContextTranslateCTM(aContext, tx, ty) { - aContext.translate(tx, ty); + aContext.translate(tx, ty); } -// MODIFICATION: Removed legacy mathematical fallback macros (scale_rotate, rotate_scale, eigen). -// MODIFICATION: Removed CPFeatureIsCompatible(CPJavaScriptCanvasTransformFeature) branch. -// Modern target environments inherently support native canvas transformations. - -function CGContextConcatCTM(aContext, anAffineTransform) +var scale_rotate = function(a, b, c, d) { - aContext.transform(anAffineTransform.a, anAffineTransform.b, anAffineTransform.c, anAffineTransform.d, anAffineTransform.tx, anAffineTransform.ty); + var sign = (a * d < 0.0 || b * c > 0.0) ? -1.0 : 1.0, + a2 = (ATAN2(b, d) + ATAN2(-sign * c, sign * a)) / 2.0, + cos = COS(a2), + sin = SIN(a2); + + if (cos === 0) + { + sx = -c / sin; + sy = b / sin; + } + else if (sin === 0) + { + sx = a / cos; + sy = d / cos; + } + else + { + abs_cos = ABS(cos); + abs_sin = ABS(sin); + + sx = (abs_cos * a / cos + abs_sin * -c / sin) / (abs_cos + abs_sin); + sy = (abs_cos * d / cos + abs_sin * b / sin) / (abs_cos + abs_sin); + } +}; + +var rotate_scale = function(a, b, c, d) +{ + var sign = (a * d < 0.0 || b * c > 0.0) ? -1.0 : 1.0, + a1 = (ATAN2(sign * b, sign * a) + ATAN2(-c, d)) / 2.0, + cos = COS(a1), + sin = SIN(a1); + + if (cos === 0) + { + sx = b / sin; + sy = -c / sin; + } + else if (sin === 0) + { + sx = a / cos; + sy = d / cos; + } + else + { + abs_cos = ABS(cos); + abs_sin = ABS(sin); + + sx = (abs_cos * a / cos + abs_sin * b / sin) / (abs_cos + abs_sin); + sy = (abs_cos * d / cos + abs_sin * -c / sin) / (abs_cos + abs_sin); + } +}; + +function eigen(anAffineTransform) +{ + CPLog.warn("Unimplemented function: eigen"); +} + + +if (CPFeatureIsCompatible(CPJavaScriptCanvasTransformFeature)) +{ + +CGContextConcatCTM = function(aContext, anAffineTransform) +{ + aContext.transform(anAffineTransform.a, anAffineTransform.b, anAffineTransform.c, anAffineTransform.d, anAffineTransform.tx, anAffineTransform.ty); +}; + +} +else +{ + +CGContextConcatCTM = function(aContext, anAffineTransform) +{ + var a = anAffineTransform.a, + b = anAffineTransform.b, + c = anAffineTransform.c, + d = anAffineTransform.d, + tx = anAffineTransform.tx, + ty = anAffineTransform.ty, + sx = 1.0, + sy = 1.0, + a1 = 0.0, + a2 = 0.0; + + // Detect the simple case of just scaling. + if (b === 0.0 && c === 0.0) + { + sx = a; + sy = d; + } + + // a scale followed by a rotate + else if (a * b === -c * d) + { + scale_rotate(a, b, c, d) + } + + // rotate, then scale. + else if (a * c === -b * d) + { + rotate_scale(a, b, c, d) + } + else + { + var transpose = CGAffineTransformMake(a, c, b, d, 0.0, 0.0), // inline + u = eigen(CGAffineTransformConcat(anAffineTransform, transpose)), + v = eigen(CGAffineTransformConcat(transpose, anAffineTransform)), + U = CGAffineTransformMake(u.vector_1.x, u.vector_2.x, u.vector_1.y, u.vector_2.y, 0.0, 0.0), // inline + VT = CGAffineTransformMake(v.vector_1.x, v.vector_1.y, v.vector_2.x, v.vector_2.y, 0.0, 0.0), + S = CGAffineTransformConcat(CGAffineTransformConcat(CGAffineTransformInvert(U), anAffineTransform), CGAffineTransformInvert(VT)); + + a = VT.a; + b = VT.b; + c = VT.c; + d = VT.d; + scale_rotate(a, b, c, d); + S.a *= sx; + S.d *= sy; + a = U.a; + b = U.b; + c = U.c; + d = U.d; + rotate_scale(a, b, c, d); + sx = S.a * sx; + sy = S.d * sy; + } + + if (tx != 0 || ty != 0) + CGContextTranslateCTM(aContext, tx, ty); + if (a1 != 0.0) + CGContextRotateCTM(aContext, a1); + if (sx != 1.0 || sy != 1.0) + CGContextScaleCTM(aContext, sx, sy); + if (a2 != 0.0) + CGContextRotateCTM(aContext, a2); +}; + } function CGContextDrawImage(aContext, aRect, anImage) { - aContext.drawImage(anImage._image, CGRectGetMinX(aRect), CGRectGetMinY(aRect), CGRectGetWidth(aRect), CGRectGetHeight(aRect)); - aContext.hasPath = NO; + aContext.drawImage(anImage._image, CGRectGetMinX(aRect), CGRectGetMinY(aRect), CGRectGetWidth(aRect), CGRectGetHeight(aRect)); + aContext.hasPath = NO; } function to_string(aColor) { - // MODIFICATION: Replaced macro ROUND with standard Math.round. - return "rgba(" + Math.round(aColor.components[0] * 255) + ", " + Math.round(aColor.components[1] * 255) + ", " + Math.round(255 * aColor.components[2]) + ", " + aColor.components[3] + ")"; + return "rgba(" + ROUND(aColor.components[0] * 255) + ", " + ROUND(aColor.components[1] * 255) + ", " + ROUND(255 * aColor.components[2]) + ", " + aColor.components[3] + ")"; } function CGContextDrawLinearGradient(aContext, aGradient, aStartPoint, anEndPoint, options) { - const colors = aGradient.colors; - const linearGradient = aContext.createLinearGradient(aStartPoint.x, aStartPoint.y, anEndPoint.x, anEndPoint.y); - let count = colors.length; + var colors = aGradient.colors, + count = colors.length, + linearGradient = aContext.createLinearGradient(aStartPoint.x, aStartPoint.y, anEndPoint.x, anEndPoint.y); - while (count--) - { - linearGradient.addColorStop(aGradient.locations[count], to_string(colors[count])); - } + while (count--) + linearGradient.addColorStop(aGradient.locations[count], to_string(colors[count])); - aContext.fillStyle = linearGradient; - aContext.fill(); - aContext.hasPath = NO; + aContext.fillStyle = linearGradient; + aContext.fill(); + aContext.hasPath = NO; } function CGContextDrawRadialGradient(aContext, aGradient, aStartCenter, aStartRadius, anEndCenter, anEndRadius, options) { - const colors = aGradient.colors; - const linearGradient = aContext.createRadialGradient(aStartCenter.x, aStartCenter.y, aStartRadius, anEndCenter.x, anEndCenter.y, anEndRadius); - let count = colors.length; + var colors = aGradient.colors, + count = colors.length, + linearGradient = aContext.createRadialGradient(aStartCenter.x, aStartCenter.y, aStartRadius, anEndCenter.x, anEndCenter.y, anEndRadius); - while (count--) - { - linearGradient.addColorStop(aGradient.locations[count], to_string(colors[count])); - } + while (count--) + linearGradient.addColorStop(aGradient.locations[count], to_string(colors[count])); - aContext.fillStyle = linearGradient; - aContext.fill(); - aContext.hasPath = NO; + aContext.fillStyle = linearGradient; + aContext.fill(); + aContext.hasPath = NO; } function CGBitmapGraphicsContextCreate() { - const DOMElement = document.createElement("canvas"); - const context = DOMElement.getContext("2d"); + var DOMElement = document.createElement("canvas"), + context = DOMElement.getContext("2d"); - context.DOMElement = DOMElement; + context.DOMElement = DOMElement; - // canvas gives us no way to query whether the path is empty or not, so we have to track it ourselves - context.hasPath = NO; + // canvas gives us no way to query whether the path is empty or not, so we have to track it ourselves + context.hasPath = NO; - return context; + return context; } From 4f29b77d652acec5f5cbb55c49bf86f74755d7d5 Mon Sep 17 00:00:00 2001 From: David Richardson Date: Thu, 27 Aug 2026 11:20:20 -0600 Subject: [PATCH 2/4] Revert "Merge pull request #3261 from enquora/modernize-CPPlatformpasteboard" This reverts commit 10351c3d4a898b4d6a0f0b74e4f4c616847b66bb, reversing changes made to 5bdac195fc0495e05ac241e3ff41b292e09009f7. --- AppKit/Platform/DOM/CPPlatformPasteboard.j | 661 ++++++++++----------- 1 file changed, 322 insertions(+), 339 deletions(-) diff --git a/AppKit/Platform/DOM/CPPlatformPasteboard.j b/AppKit/Platform/DOM/CPPlatformPasteboard.j index 6bc533eda..102feb670 100644 --- a/AppKit/Platform/DOM/CPPlatformPasteboard.j +++ b/AppKit/Platform/DOM/CPPlatformPasteboard.j @@ -37,24 +37,22 @@ #if PLATFORM(DOM) -// MODIFICATION: Removed #define macros for SUPPRESS_CAPPUCCINO_CUT/PASTE. -// Property assignments are now inlined directly at call sites for readability. +#define SUPPRESS_CAPPUCCINO_CUT_FOR_EVENT(anEvent) anEvent._suppressCappuccinoCut = YES +#define SUPPRESS_CAPPUCCINO_PASTE_FOR_EVENT(anEvent) anEvent._suppressCappuccinoPaste = YES var hasEditableTarget = function(aDOMEvent) { - // MODIFICATION: Removed IE 'srcElement' fallback. Replaced 'var' with 'let'. - let target = aDOMEvent.target; + var target = aDOMEvent.target || aDOMEvent.srcElement; - if (!target) - return NO; + if (!target) + return NO; - if (target.contentEditable == "true") - return YES; + if (target.contentEditable == "true") + return YES; - // MODIFICATION: Replaced 'var' with 'let'. - let nodeName = target.nodeName.toUpperCase(); + var nodeName = target.nodeName.toUpperCase(); - return nodeName === "INPUT" || nodeName == "TEXTAREA"; + return nodeName === "INPUT" || nodeName == "TEXTAREA"; } /* @@ -65,481 +63,466 @@ var hasEditableTarget = function(aDOMEvent) */ @implementation CPPlatformPasteboard : CPObject { - DOMWindow _DOMWindow; + DOMWindow _DOMWindow; - DOMElement _DOMPasteboardElement; + DOMElement _DOMPasteboardElement; - BOOL supportsNativeCopyAndPaste; - BOOL hasBugWhichPreventsNonEditablePaste; - BOOL hasBugWhichPreventsNonEditablePasteRedirect; + BOOL supportsNativeCopyAndPaste; + BOOL hasBugWhichPreventsNonEditablePaste; + BOOL hasBugWhichPreventsNonEditablePasteRedirect; - BOOL currentEventIsNativePasteEvent; - BOOL currentEventIsNativeCopyOrCutEvent; - BOOL currentEventShouldBeSuppressed; - BOOL currentEventShouldDefinitelyBubble; - BOOL currentEventShouldDefinitelyNotBubble; + BOOL currentEventIsNativePasteEvent; + BOOL currentEventIsNativeCopyOrCutEvent; + BOOL currentEventShouldBeSuppressed; + BOOL currentEventShouldDefinitelyBubble; + BOOL currentEventShouldDefinitelyNotBubble; - BOOL _ignoreNativeCopyOrCutEvent; - BOOL _ignoreNativePastePreparation; + BOOL _ignoreNativeCopyOrCutEvent; + BOOL _ignoreNativePastePreparation; } - (id)init { - if (self = [super init]) - { - supportsNativeCopyAndPaste = CPFeatureIsCompatible(CPJavaScriptClipboardEventsFeature); - hasBugWhichPreventsNonEditablePaste = CPPlatformHasBug(CPJavaScriptPasteRequiresEditableTarget); - hasBugWhichPreventsNonEditablePasteRedirect = CPPlatformHasBug(CPJavaScriptPasteCantRefocus); - } + if (self = [super init]) + { + supportsNativeCopyAndPaste = CPFeatureIsCompatible(CPJavaScriptClipboardEventsFeature); + hasBugWhichPreventsNonEditablePaste = CPPlatformHasBug(CPJavaScriptPasteRequiresEditableTarget); + hasBugWhichPreventsNonEditablePasteRedirect = CPPlatformHasBug(CPJavaScriptPasteCantRefocus); + } - return self; + return self; } - (void)setDOMWindow:(DOMWindow)aDOMWindow { - if (_DOMWindow === aDOMWindow) - return; + if (_DOMWindow === aDOMWindow) + return; - if (_DOMWindow) - [self destroyDOMElements]; + if (_DOMWindow) + [self destroyDOMElements]; - _DOMWindow = aDOMWindow; + _DOMWindow = aDOMWindow; - if (_DOMWindow) - [self createDOMElements]; + if (_DOMWindow) + [self createDOMElements]; } - (void)createDOMElements { - // MODIFICATION: Replaced 'var' with 'const'/'let'. - const theDocument = _DOMWindow.document, - _DOMBodyElement = theDocument.getElementById("cappuccino-body") || theDocument.body; + var theDocument = _DOMWindow.document, + _DOMBodyElement = theDocument.getElementById("cappuccino-body") || theDocument.body; - // Create Native Pasteboard handler. - _DOMPasteboardElement = theDocument.createElement("textarea"); + // Create Native Pasteboard handler. + _DOMPasteboardElement = theDocument.createElement("textarea"); - _DOMPasteboardElement.style.position = "absolute"; - _DOMPasteboardElement.style.top = "-10000px"; - _DOMPasteboardElement.style.zIndex = "999"; - _DOMPasteboardElement.className = "cpdontremove"; + _DOMPasteboardElement.style.position = "absolute"; + _DOMPasteboardElement.style.top = "-10000px"; + _DOMPasteboardElement.style.zIndex = "999"; + _DOMPasteboardElement.className = "cpdontremove"; - _DOMBodyElement.appendChild(_DOMPasteboardElement); + _DOMBodyElement.appendChild(_DOMPasteboardElement); - _DOMPasteboardElement.blur(); + _DOMPasteboardElement.blur(); - // MODIFICATION: Replaced 'var' with 'const'. - const copyEventCallback = function (anEvent) { return [self beforeCopyEvent:anEvent]; }, - nativeBeforeClipboardEventCallback = function (anEvent) { return [self nativeBeforeClipboardEvent:anEvent]; }, - nativeCopyOrCutEventCallback = function (anEvent) { return [self nativeCopyOrCutEvent:anEvent]; }, - pasteEventCallback = function (anEvent) { return [self beforePasteEvent:anEvent]; }, - nativePasteEventCallback = function (anEvent) { return [self nativePasteEvent:anEvent]; }; + var copyEventCallback = function (anEvent) { return [self beforeCopyEvent:anEvent]; }, + nativeBeforeClipboardEventCallback = function (anEvent) { return [self nativeBeforeClipboardEvent:anEvent]; }, + nativeCopyOrCutEventCallback = function (anEvent) { return [self nativeCopyOrCutEvent:anEvent]; }, + pasteEventCallback = function (anEvent) { return [self beforePasteEvent:anEvent]; }, + nativePasteEventCallback = function (anEvent) { return [self nativePasteEvent:anEvent]; }; - // MODIFICATION: Removed the 'if (theDocument.addEventListener)' check and the IE 'else' block. - // Modern browsers natively support addEventListener. - if (supportsNativeCopyAndPaste) - { - _DOMWindow.addEventListener("beforecopy", nativeBeforeClipboardEventCallback, NO); - _DOMWindow.addEventListener("beforecut", nativeBeforeClipboardEventCallback, NO); - _DOMWindow.addEventListener("beforepaste", nativeBeforeClipboardEventCallback, NO); - _DOMWindow.addEventListener("copy", nativeCopyOrCutEventCallback, NO); - _DOMWindow.addEventListener("cut", nativeCopyOrCutEventCallback, NO); - _DOMWindow.addEventListener("paste", nativePasteEventCallback, NO); - } - else - { - theDocument.addEventListener("beforepaste", pasteEventCallback, NO); - theDocument.addEventListener("beforecopy", copyEventCallback, NO); - theDocument.addEventListener("beforecut", copyEventCallback, NO); - } + if (theDocument.addEventListener) + { + if (supportsNativeCopyAndPaste) + { + _DOMWindow.addEventListener("beforecopy", nativeBeforeClipboardEventCallback, NO); + _DOMWindow.addEventListener("beforecut", nativeBeforeClipboardEventCallback, NO); + _DOMWindow.addEventListener("beforepaste", nativeBeforeClipboardEventCallback, NO); + _DOMWindow.addEventListener("copy", nativeCopyOrCutEventCallback, NO); + _DOMWindow.addEventListener("cut", nativeCopyOrCutEventCallback, NO); + _DOMWindow.addEventListener("paste", nativePasteEventCallback, NO); + } + else + { + theDocument.addEventListener("beforepaste", pasteEventCallback, NO); + theDocument.addEventListener("beforecopy", copyEventCallback, NO); + theDocument.addEventListener("beforecut", copyEventCallback, NO); + } - _DOMWindow.addEventListener("unload", function() - { - if (supportsNativeCopyAndPaste) - { - _DOMWindow.removeEventListener("beforecopy", nativeBeforeClipboardEventCallback, NO); - _DOMWindow.removeEventListener("beforecut", nativeBeforeClipboardEventCallback, NO); - _DOMWindow.removeEventListener("beforepaste", nativeBeforeClipboardEventCallback, NO); - _DOMWindow.removeEventListener("copy", nativeCopyOrCutEventCallback, NO); - _DOMWindow.removeEventListener("cut", nativeCopyOrCutEventCallback, NO); - _DOMWindow.removeEventListener("paste", nativePasteEventCallback, NO); - } - else - { - theDocument.removeEventListener("beforepaste", pasteEventCallback, NO); - theDocument.removeEventListener("beforecopy", copyEventCallback, NO); - theDocument.removeEventListener("beforecut", copyEventCallback, NO); - } - }, NO); + _DOMWindow.addEventListener("unload", function() + { + if (supportsNativeCopyAndPaste) + { + _DOMWindow.removeEventListener("beforecopy", nativeBeforeClipboardEventCallback, NO); + _DOMWindow.removeEventListener("beforecut", nativeBeforeClipboardEventCallback, NO); + _DOMWindow.removeEventListener("beforepaste", nativeBeforeClipboardEventCallback, NO); + _DOMWindow.removeEventListener("copy", nativeCopyOrCutEventCallback, NO); + _DOMWindow.removeEventListener("cut", nativeCopyOrCutEventCallback, NO); + _DOMWindow.removeEventListener("paste", nativePasteEventCallback, NO); + } + else + { + theDocument.removeEventListener("beforepaste", pasteEventCallback, NO); + theDocument.removeEventListener("beforecopy", copyEventCallback, NO); + theDocument.removeEventListener("beforecut", copyEventCallback, NO); + } + }, NO); + } + else + { + // TODO If we wanted IE 8 and lower copy and paste it'd go here. + } } - (void)destroyDOMElements { - // MODIFICATION: Replaced 'var' with 'const'. - const theDocument = _DOMWindow.document, - _DOMBodyElement = theDocument.getElementById("cappuccino-body") || theDocument.body; + var theDocument = _DOMWindow.document, + _DOMBodyElement = theDocument.getElementById("cappuccino-body") || theDocument.body; - _DOMBodyElement.removeChild(_DOMPasteboardElement); - _DOMPasteboardElement = nil; + _DOMBodyElement.removeChild(_DOMPasteboardElement); + _DOMPasteboardElement = nil; } - (void)windowMaySendKeyEvent:(CPEvent)anEvent { - // Reset our opinions. - currentEventIsNativePasteEvent = NO; - currentEventIsNativeCopyOrCutEvent = NO; - currentEventShouldBeSuppressed = NO; - currentEventShouldDefinitelyNotBubble = NO; - currentEventShouldDefinitelyBubble = NO; + // Reset our opinions. + currentEventIsNativePasteEvent = NO; + currentEventIsNativeCopyOrCutEvent = NO; + currentEventShouldBeSuppressed = NO; + currentEventShouldDefinitelyNotBubble = NO; + currentEventShouldDefinitelyBubble = NO; - if (!anEvent) - return; + if (!anEvent) + return; - if ([anEvent type] !== CPKeyDown) - { - // Reset these flags on key up. - _ignoreNativePastePreparation = NO; - _ignoreNativeCopyOrCutEvent = NO; - return; - } + if ([anEvent type] !== CPKeyDown) + { + // Reset these flags on key up. + _ignoreNativePastePreparation = NO; + _ignoreNativeCopyOrCutEvent = NO; + return; + } - // MODIFICATION: Replaced 'var' with 'const'. - const modifierFlags = [anEvent modifierFlags]; + var modifierFlags = [anEvent modifierFlags]; - if (!(modifierFlags & (CPControlKeyMask | CPCommandKeyMask))) - return; + if (!(modifierFlags & (CPControlKeyMask | CPCommandKeyMask))) + return; - const aDOMEvent = anEvent._DOMEvent, - characters = [anEvent characters], - mayRequireDOMPasteboardElement = [self _mayRequireDOMPasteboardElementHack:aDOMEvent flags:modifierFlags]; + var aDOMEvent = anEvent._DOMEvent, + characters = [anEvent characters], + mayRequireDOMPasteboardElement = [self _mayRequireDOMPasteboardElementHack:aDOMEvent flags:modifierFlags]; - if (characters === "v" && mayRequireDOMPasteboardElement) - { - if (supportsNativeCopyAndPaste && hasBugWhichPreventsNonEditablePaste && hasBugWhichPreventsNonEditablePasteRedirect && !hasEditableTarget(aDOMEvent)) - { - // You can't paste from the system clipboard into a non-editable area in Safari, neither using native - // copy and paste nor our _DOMPasteboardElement hack. We will paste from the Cappuccino pasteboard only - // and allow Safari to "beep" to indicate something went wrong. + if (characters === "v" && mayRequireDOMPasteboardElement) + { + if (supportsNativeCopyAndPaste && hasBugWhichPreventsNonEditablePaste && hasBugWhichPreventsNonEditablePasteRedirect && !hasEditableTarget(aDOMEvent)) + { + // You can't paste from the system clipboard into a non-editable area in Safari, neither using native + // copy and paste nor our _DOMPasteboardElement hack. We will paste from the Cappuccino pasteboard only + // and allow Safari to "beep" to indicate something went wrong. - // The key down will not result in a paste event being sent by Safari, so it's not a paste event. - currentEventIsNativePasteEvent = NO; - // Yes to get the beep. - currentEventShouldDefinitelyBubble = YES; - } - else if (!(supportsNativeCopyAndPaste || hasBugWhichPreventsNonEditablePaste) && !_ignoreNativePastePreparation) - { - // We don't support native copy and paste so we must focus the _DOMPasteboardElement to receive the - // paste content. This needs to be done at keyDown time (or in the case of modern Safari, before the keyDown, - // which unfortunately isn't possible. See above.) - _DOMPasteboardElement.focus(); - _DOMPasteboardElement.select(); - _DOMPasteboardElement.value = ""; - _DOMWindow.setNativeTimeout(function () { [self _checkDOMPasteboardElement]; }, 0); + // The key down will not result in a paste event being sent by Safari, so it's not a paste event. + currentEventIsNativePasteEvent = NO; + // Yes to get the beep. + currentEventShouldDefinitelyBubble = YES; + } + else if (!(supportsNativeCopyAndPaste || hasBugWhichPreventsNonEditablePaste) && !_ignoreNativePastePreparation) + { + // We don't support native copy and paste so we must focus the _DOMPasteboardElement to receive the + // paste content. This needs to be done at keyDown time (or in the case of modern Safari, before the keyDown, + // which unfortunately isn't possible. See above.) + _DOMPasteboardElement.focus(); + _DOMPasteboardElement.select(); + _DOMPasteboardElement.value = ""; + _DOMWindow.setNativeTimeout(function () { [self _checkDOMPasteboardElement]; }, 0); - currentEventIsNativePasteEvent = YES; - } - else if (supportsNativeCopyAndPaste) - currentEventIsNativePasteEvent = YES; + currentEventIsNativePasteEvent = YES; + } + else if (supportsNativeCopyAndPaste) + currentEventIsNativePasteEvent = YES; - if (currentEventIsNativePasteEvent) - { - // We need the event to propagate or nothing will be pasted. - currentEventShouldDefinitelyBubble = YES; + if (currentEventIsNativePasteEvent) + { + // We need the event to propagate or nothing will be pasted. + currentEventShouldDefinitelyBubble = YES; - // And we don't send the keydown because either our native paste envent handler will send it, - // or the _checkDOMPasteboardElement check will. - currentEventShouldBeSuppressed = YES; - } - } - else if ((characters == "c" || characters == "x") && mayRequireDOMPasteboardElement) - { - currentEventIsNativeCopyOrCutEvent = YES; + // And we don't send the keydown because either our native paste envent handler will send it, + // or the _checkDOMPasteboardElement check will. + currentEventShouldBeSuppressed = YES; + } + } + else if ((characters == "c" || characters == "x") && mayRequireDOMPasteboardElement) + { + currentEventIsNativeCopyOrCutEvent = YES; - // If _ignoreNativeCopyOrCutEvent, we already handled the copy/cut in beforeCopyEvent:. - // If supportsNativeCopyAndPaste, we will be handling it in the nativebeforeCopyEvent: handler. - // In both of those cases we don't want to send the event on keydown too, or we'll get 2X copy/cut operations. - if (supportsNativeCopyAndPaste || _ignoreNativeCopyOrCutEvent) - currentEventShouldBeSuppressed = YES; - } + // If _ignoreNativeCopyOrCutEvent, we already handled the copy/cut in beforeCopyEvent:. + // If supportsNativeCopyAndPaste, we will be handling it in the nativebeforeCopyEvent: handler. + // In both of those cases we don't want to send the event on keydown too, or we'll get 2X copy/cut operations. + if (supportsNativeCopyAndPaste || _ignoreNativeCopyOrCutEvent) + currentEventShouldBeSuppressed = YES; + } - if (CPFeatureIsCompatible(CPBackspaceTriggersPageBack) && characters === CPDeleteCharacter && !hasEditableTarget(aDOMEvent)) - currentEventShouldBeSuppressed = YES; + if (CPFeatureIsCompatible(CPBackspaceTriggersPageBack) && characters === CPDeleteCharacter && !hasEditableTarget(aDOMEvent)) + currentEventShouldBeSuppressed = YES; - if (!currentEventShouldBeSuppressed) - { - // MODIFICATION: Inlined the SUPPRESS_CAPPUCCINO_PASTE_FOR_EVENT macro. - if (characters === "v") - anEvent._suppressCappuccinoPaste = YES; - // MODIFICATION: Inlined the SUPPRESS_CAPPUCCINO_CUT_FOR_EVENT macro. - else if (characters === "x") - anEvent._suppressCappuccinoCut = YES; - } + if (!currentEventShouldBeSuppressed) + { + if (characters === "v") + SUPPRESS_CAPPUCCINO_PASTE_FOR_EVENT(anEvent); + else if (characters === "x") + SUPPRESS_CAPPUCCINO_CUT_FOR_EVENT(anEvent); + } } - (BOOL)windowShouldSuppressKeyEvent { - return currentEventShouldBeSuppressed; + return currentEventShouldBeSuppressed; } - (void)windowDidSendKeyEvent:(CPEvent)anEvent { - // Now that the copy event has been sent through the Cappuccino event stack, we can load any Cappuccino - // pasteboard string into our DOMPasteboardElement hack, if necessary. - if (!supportsNativeCopyAndPaste && currentEventIsNativeCopyOrCutEvent) - [self _primeDOMPasteboardElement]; + // Now that the copy event has been sent through the Cappuccino event stack, we can load any Cappuccino + // pasteboard string into our DOMPasteboardElement hack, if necessary. + if (!supportsNativeCopyAndPaste && currentEventIsNativeCopyOrCutEvent) + [self _primeDOMPasteboardElement]; } - (BOOL)windowShouldStopPropagation { - return currentEventShouldDefinitelyNotBubble; + return currentEventShouldDefinitelyNotBubble; } - (BOOL)windowShouldNotStopPropagation { - return currentEventShouldDefinitelyBubble; + return currentEventShouldDefinitelyBubble; } - (CPEvent)_fakeClipboardEvent:(DOMEvent)aDOMEvent type:(CPString)aType { - // MODIFICATION: Replaced 'var' with 'const'. - const keyCode = aType === "x" ? CPKeyCodes.X : (aType === "c" ? CPKeyCodes.C : CPKeyCodes.V), - characters = aType, - timestamp = [CPEvent currentTimestamp], // fake event, might as well use current timestamp - windowNumber = [[CPApp keyWindow] windowNumber], - modifierFlags = CPPlatformActionKeyMask, - location = [[CPApp currentEvent] locationInWindow], - anEvent = [CPEvent keyEventWithType:CPKeyDown location:location modifierFlags:modifierFlags - timestamp:timestamp windowNumber:windowNumber context:nil - characters:characters charactersIgnoringModifiers:characters isARepeat:NO keyCode:keyCode isActionKey:YES]; + var keyCode = aType === "x" ? CPKeyCodes.X : (aType === "c" ? CPKeyCodes.C : CPKeyCodes.V), + characters = aType, + timestamp = [CPEvent currentTimestamp], // fake event, might as well use current timestamp + windowNumber = [[CPApp keyWindow] windowNumber], + modifierFlags = CPPlatformActionKeyMask, + location = [[CPApp currentEvent] locationInWindow], + anEvent = [CPEvent keyEventWithType:CPKeyDown location:location modifierFlags:modifierFlags + timestamp:timestamp windowNumber:windowNumber context:nil + characters:characters charactersIgnoringModifiers:characters isARepeat:NO keyCode:keyCode isActionKey:YES]; - anEvent._data1 = @{ "simulated": YES }; - anEvent._DOMEvent = aDOMEvent; + anEvent._data1 = @{ "simulated": YES }; + anEvent._DOMEvent = aDOMEvent; - return anEvent; + return anEvent; } - (void)beforeCopyEvent:(DOMEvent)aDOMEvent { - if ([self _mayRequireDOMPasteboardElementHack:aDOMEvent flags:CPPlatformActionKeyMask] && !_ignoreNativeCopyOrCutEvent) - { - // we have to send out a fake copy or cut event so that we can force the copy/cut mechanisms to take place - // MODIFICATION: Replaced 'var' with 'const'. - const anEvent = [self _fakeClipboardEvent:aDOMEvent type:(aDOMEvent.type === "beforecut" ? "x" : "c")]; + if ([self _mayRequireDOMPasteboardElementHack:aDOMEvent flags:CPPlatformActionKeyMask] && !_ignoreNativeCopyOrCutEvent) + { + // we have to send out a fake copy or cut event so that we can force the copy/cut mechanisms to take place + var anEvent = [self _fakeClipboardEvent:aDOMEvent type:(aDOMEvent.type === "beforecut" ? "x" : "c")]; - [CPApp sendEvent:anEvent]; + [CPApp sendEvent:anEvent]; - // Once we've sent it, we can load the copy information into the pasteboard hack. - [self _primeDOMPasteboardElement]; + // Once we've sent it, we can load the copy information into the pasteboard hack. + [self _primeDOMPasteboardElement]; - //then we have to IGNORE the real keyboard event to prevent a double copy - //safari also sends the beforecopy event twice, so we additionally check here and prevent two events - _ignoreNativeCopyOrCutEvent = YES; - } + //then we have to IGNORE the real keyboard event to prevent a double copy + //safari also sends the beforecopy event twice, so we additionally check here and prevent two events + _ignoreNativeCopyOrCutEvent = YES; + } - [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; + [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; } - (BOOL)beforePasteEvent:(DOMEvent)aDOMEvent { - // Set up to capture the paste in a temporary input field. We'll send the event after capture. - if ([self _mayRequireDOMPasteboardElementHack:aDOMEvent flags:CPPlatformActionKeyMask]) - { - _DOMPasteboardElement.focus(); - _DOMPasteboardElement.select(); - _DOMPasteboardElement.value = ""; + // Set up to capture the paste in a temporary input field. We'll send the event after capture. + if ([self _mayRequireDOMPasteboardElementHack:aDOMEvent flags:CPPlatformActionKeyMask]) + { + _DOMPasteboardElement.focus(); + _DOMPasteboardElement.select(); + _DOMPasteboardElement.value = ""; - _ignoreNativePastePreparation = YES; - } + _ignoreNativePastePreparation = YES; + } - [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; + [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; } /* - Return true if the event may be a copy and paste event, but the target is not an input or text area. - */ +Return true if the event may be a copy and paste event, but the target is not an input or text area. +*/ - (BOOL)_mayRequireDOMPasteboardElementHack:(DOMEvent)aDOMEvent flags:(unsigned)modifierFlags { - return !hasEditableTarget(aDOMEvent) && (modifierFlags & CPPlatformActionKeyMask); + return !hasEditableTarget(aDOMEvent) && (modifierFlags & CPPlatformActionKeyMask); } - (void)_primeDOMPasteboardElement { - // MODIFICATION: Replaced 'var' with 'const'. - const pasteboard = [CPPasteboard generalPasteboard], - types = [pasteboard types]; + var pasteboard = [CPPasteboard generalPasteboard], + types = [pasteboard types]; - if (types.length) - { - if ([types indexOfObjectIdenticalTo:CPStringPboardType] !== CPNotFound) - _DOMPasteboardElement.value = [pasteboard stringForType:CPStringPboardType]; - else - _DOMPasteboardElement.value = [pasteboard _generateStateUID]; + if (types.length) + { + if ([types indexOfObjectIdenticalTo:CPStringPboardType] !== CPNotFound) + _DOMPasteboardElement.value = [pasteboard stringForType:CPStringPboardType]; + else + _DOMPasteboardElement.value = [pasteboard _generateStateUID]; - _DOMPasteboardElement.focus(); - _DOMPasteboardElement.select(); + _DOMPasteboardElement.focus(); + _DOMPasteboardElement.select(); - window.setNativeTimeout(function() { [self _clearDOMPasteboardElement]; }, 0); - } + window.setNativeTimeout(function() { [self _clearDOMPasteboardElement]; }, 0); + } } - (void)_checkDOMPasteboardElement { - if (supportsNativeCopyAndPaste) - { - [self _clearDOMPasteboardElement]; - return; - } + if (supportsNativeCopyAndPaste) + { + [self _clearDOMPasteboardElement]; + return; + } - // MODIFICATION: Replaced 'var' with 'const'. - const value = _DOMPasteboardElement.value; + var value = _DOMPasteboardElement.value; - if ([value length]) - { - // MODIFICATION: Replaced 'var' with 'const'. - const pasteboard = [CPPasteboard generalPasteboard], - cappString = [pasteboard stringForType:CPStringPboardType]; + if ([value length]) + { + var pasteboard = [CPPasteboard generalPasteboard], + cappString = [pasteboard stringForType:CPStringPboardType]; - if (cappString != value) - { - [pasteboard declareTypes:[CPStringPboardType] owner:self]; - [pasteboard setString:value forType:CPStringPboardType]; - } - } + if (cappString != value) + { + [pasteboard declareTypes:[CPStringPboardType] owner:self]; + [pasteboard setString:value forType:CPStringPboardType]; + } + } - [self _clearDOMPasteboardElement]; + [self _clearDOMPasteboardElement]; - [CPApp sendEvent:[self _fakeClipboardEvent:nil type:@"v"]]; + [CPApp sendEvent:[self _fakeClipboardEvent:nil type:@"v"]]; - [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; + [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; } - (void)_clearDOMPasteboardElement { - _DOMPasteboardElement.value = ""; - _DOMPasteboardElement.blur(); + _DOMPasteboardElement.value = ""; + _DOMPasteboardElement.blur(); } - (BOOL)nativeBeforeClipboardEvent:(DOMEvent)aDOMEvent { - // Our job here is to return "false" if the given clipboard operation should be enabled even in a situation where - // the browser might normally grey the option out. + // Our job here is to return "false" if the given clipboard operation should be enabled even in a situation where + // the browser might normally grey the option out. - // Allow these fields to do their own thing. - if (hasEditableTarget(aDOMEvent)) - return true; + // Allow these fields to do their own thing. + if (hasEditableTarget(aDOMEvent)) + return true; - // MODIFICATION: Replaced 'var' with 'let'. - let returnValue = YES; + var returnValue = YES; - switch (aDOMEvent.type) - { - case "beforecopy": - returnValue = !([CPApp targetForAction:@selector(copy:)]); - break; - case "beforecut": - returnValue = !([CPApp targetForAction:@selector(cut:)]); - break; - case "beforepaste": - returnValue = !([CPApp targetForAction:@selector(paste:)]); - break; - } + switch (aDOMEvent.type) + { + case "beforecopy": + returnValue = !([CPApp targetForAction:@selector(copy:)]); + break; + case "beforecut": + returnValue = !([CPApp targetForAction:@selector(cut:)]); + break; + case "beforepaste": + returnValue = !([CPApp targetForAction:@selector(paste:)]); + break; + } - if (!returnValue) - _CPDOMEventStop(aDOMEvent, self); + if (!returnValue) + _CPDOMEventStop(aDOMEvent, self); - return returnValue; + return returnValue; } - (BOOL)nativePasteEvent:(DOMEvent)aDOMEvent { - // This shouldn't happen. - if (!supportsNativeCopyAndPaste) - return; + // This shouldn't happen. + if (!supportsNativeCopyAndPaste) + return; - // MODIFICATION: Replaced 'var' with 'let'. - let value; - if (aDOMEvent.clipboardData && aDOMEvent.clipboardData.setData) - value = aDOMEvent.clipboardData.getData('text/plain'); - else - value = _DOMWindow.clipboardData.getData("Text"); + var value; + if (aDOMEvent.clipboardData && aDOMEvent.clipboardData.setData) + value = aDOMEvent.clipboardData.getData('text/plain'); + else + value = _DOMWindow.clipboardData.getData("Text"); - if ([value length]) - { - // MODIFICATION: Replaced 'var' with 'const'. - const pasteboard = [CPPasteboard generalPasteboard], - cappString = [pasteboard stringForType:CPStringPboardType]; + if ([value length]) + { + var pasteboard = [CPPasteboard generalPasteboard], + cappString = [pasteboard stringForType:CPStringPboardType]; - if (cappString != value) - { - [pasteboard declareTypes:[CPStringPboardType] owner:self]; - [pasteboard setString:value forType:CPStringPboardType]; - } - } + if (cappString != value) + { + [pasteboard declareTypes:[CPStringPboardType] owner:self]; + [pasteboard setString:value forType:CPStringPboardType]; + } + } - // MODIFICATION: Replaced 'var' with 'const'. - const anEvent = [self _fakeClipboardEvent:aDOMEvent type:"v"], - platformWindow = [[anEvent window] platformWindow]; + var anEvent = [self _fakeClipboardEvent:aDOMEvent type:"v"], + platformWindow = [[anEvent window] platformWindow]; - // MODIFICATION: Inlined the SUPPRESS_CAPPUCCINO_PASTE_FOR_EVENT macro. - anEvent._suppressCappuccinoPaste = YES; + SUPPRESS_CAPPUCCINO_PASTE_FOR_EVENT(anEvent); - // By default we'll stop the native handling of the event since we're handling it ourselves. However, we need to - // stop it before we send the event so that the event can overrule our choice. CPTextField for instance wants the - // default handling when focused (which is to insert into the field). - [platformWindow _propagateCurrentDOMEvent:NO]; + // By default we'll stop the native handling of the event since we're handling it ourselves. However, we need to + // stop it before we send the event so that the event can overrule our choice. CPTextField for instance wants the + // default handling when focused (which is to insert into the field). + [platformWindow _propagateCurrentDOMEvent:NO]; - [CPApp sendEvent:anEvent]; + [CPApp sendEvent:anEvent]; - [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; + [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; - if (![platformWindow _willPropagateCurrentDOMEvent]) - _CPDOMEventStop(aDOMEvent, self); + if (![platformWindow _willPropagateCurrentDOMEvent]) + _CPDOMEventStop(aDOMEvent, self); - return false; + return false; } - (BOOL)nativeCopyOrCutEvent:(DOMEvent)aDOMEvent { - // This shouldn't happen. - if (!supportsNativeCopyAndPaste) - return; + // This shouldn't happen. + if (!supportsNativeCopyAndPaste) + return; - // MODIFICATION: Replaced 'var' with 'const'. - const anEvent = [self _fakeClipboardEvent:aDOMEvent type:(aDOMEvent.type.indexOf("cut") != CPNotFound ? "x" : "c")], - platformWindow = [[anEvent window] platformWindow]; + var anEvent = [self _fakeClipboardEvent:aDOMEvent type:(aDOMEvent.type.indexOf("cut") != CPNotFound ? "x" : "c")], + platformWindow = [[anEvent window] platformWindow]; - // MODIFICATION: Inlined the SUPPRESS_CAPPUCCINO_CUT_FOR_EVENT macro. - anEvent._suppressCappuccinoCut = YES; + SUPPRESS_CAPPUCCINO_CUT_FOR_EVENT(anEvent); - [platformWindow _propagateCurrentDOMEvent:NO]; + [platformWindow _propagateCurrentDOMEvent:NO]; - // Let the app react through copy: and cut: actions. - [CPApp sendEvent:anEvent]; - [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; + // Let the app react through copy: and cut: actions. + [CPApp sendEvent:anEvent]; + [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; - // If StopDOMEventPropagation was set to NO, we don't try to write to the system clipboard. The control that did this - // wants to use the default copy/cut functionality. - if (![platformWindow _willPropagateCurrentDOMEvent]) - { - // Now the app should have written whatever it wants to have copied to the Cappuccino clipboard. So now we need - // to write it to the system board. - _CPDOMEventStop(aDOMEvent, self); + // If StopDOMEventPropagation was set to NO, we don't try to write to the system clipboard. The control that did this + // wants to use the default copy/cut functionality. + if (![platformWindow _willPropagateCurrentDOMEvent]) + { + // Now the app should have written whatever it wants to have copied to the Cappuccino clipboard. So now we need + // to write it to the system board. + _CPDOMEventStop(aDOMEvent, self); - // MODIFICATION: Replaced 'var' with 'const'. - const pasteboard = [CPPasteboard generalPasteboard]; + var pasteboard = [CPPasteboard generalPasteboard]; - if ([[pasteboard types] containsObject:CPStringPboardType]) - { - // MODIFICATION: Replaced 'var' with 'const'. - const stringValue = [pasteboard stringForType:CPStringPboardType]; + if ([[pasteboard types] containsObject:CPStringPboardType]) + { + var stringValue = [pasteboard stringForType:CPStringPboardType]; - if (aDOMEvent.clipboardData && aDOMEvent.clipboardData.setData) - aDOMEvent.clipboardData.setData('text/plain', stringValue); - else - _DOMWindow.clipboardData.setData('Text', stringValue); - } - } + if (aDOMEvent.clipboardData && aDOMEvent.clipboardData.setData) + aDOMEvent.clipboardData.setData('text/plain', stringValue); + else + _DOMWindow.clipboardData.setData('Text', stringValue); + } + } - return ![platformWindow _willPropagateCurrentDOMEvent]; + return ![platformWindow _willPropagateCurrentDOMEvent]; } @end From 4d13785119919e1595fd9f302c895c8c4b9ac37a Mon Sep 17 00:00:00 2001 From: David Richardson Date: Thu, 27 Aug 2026 11:20:30 -0600 Subject: [PATCH 3/4] Revert "Merge pull request #3257 from enquora/modernize-CABackingStore" This reverts commit 5bdac195fc0495e05ac241e3ff41b292e09009f7, reversing changes made to 8134b6dff768d750c70a36b32084acf99212b1a0. --- AppKit/CoreAnimation/CABackingStore.j | 62 +++++++++++++++++---------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/AppKit/CoreAnimation/CABackingStore.j b/AppKit/CoreAnimation/CABackingStore.j index 068e9205a..847430f62 100644 --- a/AppKit/CoreAnimation/CABackingStore.j +++ b/AppKit/CoreAnimation/CABackingStore.j @@ -21,38 +21,54 @@ */ @import "CGGeometry.j" -// REMOVED: @import "CPCompatibility.j" - Obsolete legacy compatibility layer removed. +@import "CPCompatibility.j" + +#define PIXEL(pixels) pixels + "px"; -// REMOVED: #define PIXEL(pixels) macro - Eliminated in favour of native inline string concatenation. function CABackingStoreGetContext(aBackingStore) { - return aBackingStore.context; -} + return aBackingStore.context; +}; -// REMOVED: if (CPFeatureIsCompatible(CPHTMLCanvasFeature)) - HTMLCanvasElement is natively supported across modern targets. Legacy fallback block completely removed. -function CABackingStoreCreate() +if (CPFeatureIsCompatible(CPHTMLCanvasFeature)) { - // MODERNIZED: Replaced legacy 'var' with block-scoped 'const' for immutable DOM reference. - const DOMElement = document.createElement("canvas"); - DOMElement.style.position = "absolute"; - - // FIXME: Consolidate drawImage to support this. - return { context:DOMElement.getContext("2d"), buffer:DOMElement, _image:DOMElement }; -} - -function CABackingStoreSetSize(aBackingStore, aSize) +CABackingStoreCreate = function() { - // MODERNIZED: Replaced legacy 'var' with block-scoped 'const'. - const buffer = aBackingStore.buffer; + var DOMElement = document.createElement("canvas"); - buffer.width = aSize.width; - buffer.height = aSize.height; + DOMElement.style.position = "absolute"; + + // FIXME: Consolidate drawImage to support this. + return { context:DOMElement.getContext("2d"), buffer:DOMElement, _image:DOMElement }; +}; + +CABackingStoreSetSize = function(aBackingStore, aSize) +{ + var buffer = aBackingStore.buffer; + + buffer.width = aSize.width; + buffer.height = aSize.height; + buffer.style.width = PIXEL(aSize.width); + buffer.style.height = PIXEL(aSize.height); +}; - // MODERNIZED: Replaced macro expansion with native inline evaluation. - buffer.style.width = aSize.width + "px"; - buffer.style.height = aSize.height + "px"; } +else +{ -// REMOVED: Legacy else block utilizing CGBitmapGraphicsContextCreate due to guaranteed canvas support. +CABackingStoreCreate = function() +{ + var context = CGBitmapGraphicsContextCreate(); + + context.buffer = ""; + + return { context:context }; +}; + +CABackingStoreSetSize = function(aBackingStore, aSize) +{ +}; + +} From 690b79f11304ac226e9aafb701ee6546b9570b0b Mon Sep 17 00:00:00 2001 From: David Richardson Date: Thu, 27 Aug 2026 11:20:41 -0600 Subject: [PATCH 4/4] Revert "Merge pull request #3260 from enquora/modernize-CALayer" This reverts commit 8134b6dff768d750c70a36b32084acf99212b1a0, reversing changes made to 7083f7b2685d9f0b60c533136572f2fa8a585fa1. --- AppKit/CoreAnimation/CALayer.j | 553 ++++++++++++++++----------------- 1 file changed, 273 insertions(+), 280 deletions(-) diff --git a/AppKit/CoreAnimation/CALayer.j b/AppKit/CoreAnimation/CALayer.j index 23bf5e7ce..4cd003635 100644 --- a/AppKit/CoreAnimation/CALayer.j +++ b/AppKit/CoreAnimation/CALayer.j @@ -31,40 +31,41 @@ @import "CPView.j" @import "CAMediaTimingFunction.j" -// IMPROVEMENT: Removed DOM macro in favour of explicit property access for clarity. -var CALayerGeometryBoundsMask = 1, -CALayerGeometryPositionMask = 2, -CALayerGeometryAnchorPointMask = 4, -CALayerGeometryAffineTransformMask = 8, -CALayerGeometryParentSublayerTransformMask = 16; +#define DOM(aLayer) aLayer._DOMElement -var USE_BUFFER = NO; +var CALayerGeometryBoundsMask = 1, + CALayerGeometryPositionMask = 2, + CALayerGeometryAnchorPointMask = 4, + CALayerGeometryAffineTransformMask = 8, + CALayerGeometryParentSublayerTransformMask = 16; -var CALayerFrameOriginUpdateMask = 1, -CALayerFrameSizeUpdateMask = 2, -CALayerZPositionUpdateMask = 4, -CALayerDisplayUpdateMask = 8, -CALayerCompositeUpdateMask = 16, -CALayerDOMUpdateMask = CALayerZPositionUpdateMask | CALayerFrameOriginUpdateMask | CALayerFrameSizeUpdateMask; +var USE_BUFFER = NO; -var CALayerRegisteredRunLoopUpdates = nil; +var CALayerFrameOriginUpdateMask = 1, + CALayerFrameSizeUpdateMask = 2, + CALayerZPositionUpdateMask = 4, + CALayerDisplayUpdateMask = 8, + CALayerCompositeUpdateMask = 16, + CALayerDOMUpdateMask = CALayerZPositionUpdateMask | CALayerFrameOriginUpdateMask | CALayerFrameSizeUpdateMask; + +var CALayerRegisteredRunLoopUpdates = nil; /*! @class CALayer - A CALayer is similar to a CPView, but with the ability - to have a transform applied to it. + A CALayer is similar to a CPView, but with the ability + to have a transform applied to it. - @delegate -(void)drawLayer:(CALayer)layer inContext:(CGContextRef)ctx; - If the delegate implements this method, the CALayer will - call this in place of its \c -drawInContext:. - @param layer the layer to draw for - @param ctx the context to draw on + @delegate -(void)drawLayer:(CALayer)layer inContext:(CGContextRef)ctx; + If the delegate implements this method, the CALayer will + call this in place of its \c -drawInContext:. + @param layer the layer to draw for + @param ctx the context to draw on - @delegate -(void)displayLayer:(CALayer)layer; - The delegate can override the layer's \c -display method - by implementing this method. - */ + @delegate -(void)displayLayer:(CALayer)layer; + The delegate can override the layer's \c -display method + by implementing this method. +*/ @implementation CALayer : CPObject { // Modifying the Layer Geometry @@ -126,16 +127,16 @@ var CALayerRegisteredRunLoopUpdates = nil; @global document /*! - Returns a new animation layer. - */ + Returns a new animation layer. +*/ + (CALayer)layer { return [[[self class] alloc] init]; } /*! - Initializes the animation layer. - */ + Initializes the animation layer. +*/ - (id)init { self = [super init]; @@ -184,9 +185,9 @@ var CALayerRegisteredRunLoopUpdates = nil; // Modifying the Layer Geometry /*! - Sets the bounds (origin and size) of the rectangle. - @param aBounds the new bounds for the layer - */ + Sets the bounds (origin and size) of the rectangle. + @param aBounds the new bounds for the layer +*/ - (void)setBounds:(CGRect)aBounds { if (CGRectEqualToRect(_bounds, aBounds)) @@ -201,29 +202,29 @@ var CALayerRegisteredRunLoopUpdates = nil; // _hasSublayerTransform == true will handle this for us. /*else if (!CGPointEqualToPoint(_bounds.origin, oldOrigin)) - { - var index = _sublayers.length; + { + var index = _sublayers.length; - // FIXME: This should climb the layer tree down. - while (index--) - _CALayerRecalculateGeometry(_sublayers[index], CALayerGeometryPositionMask); - }*/ + // FIXME: This should climb the layer tree down. + while (index--) + _CALayerRecalculateGeometry(_sublayers[index], CALayerGeometryPositionMask); + }*/ _CALayerRecalculateGeometry(self, CALayerGeometryBoundsMask); } /*! - Returns the layer's bound. - */ + Returns the layer's bound. +*/ - (CGRect)bounds { return _bounds; } /*! - Sets the layer's position. - @param aPosition the layer's new position - */ + Sets the layer's position. + @param aPosition the layer's new position +*/ - (void)setPosition:(CGPoint)aPosition { if (CGPointEqualToPoint(_position, aPosition)) @@ -235,17 +236,17 @@ var CALayerRegisteredRunLoopUpdates = nil; } /*! - Returns the layer's position - */ + Returns the layer's position +*/ - (CGPoint)position { return _position; } /*! - Sets the layer's z-ordering. - @param aZPosition the layer's new z-ordering - */ + Sets the layer's z-ordering. + @param aZPosition the layer's new z-ordering +*/ - (void)setZPosition:(int)aZPosition { if (_zPosition == aZPosition) @@ -257,9 +258,9 @@ var CALayerRegisteredRunLoopUpdates = nil; } /*! - Sets the layer's anchor point. The default point is [0.5, 0.5]. - @param anAnchorPoint the layer's new anchor point - */ + Sets the layer's anchor point. The default point is [0.5, 0.5]. + @param anAnchorPoint the layer's new anchor point +*/ - (void)setAnchorPoint:(CGPoint)anAnchorPoint { anAnchorPoint = CGPointMakeCopy(anAnchorPoint); @@ -281,17 +282,17 @@ var CALayerRegisteredRunLoopUpdates = nil; } /*! - Returns the layer's anchor point. - */ + Returns the layer's anchor point. +*/ - (CGPoint)anchorPoint { return _anchorPoint; } /*! - Sets the affine transform applied to this layer. - @param anAffineTransform the new affine transform - */ + Sets the affine transform applied to this layer. + @param anAffineTransform the new affine transform +*/ - (void)setAffineTransform:(CGAffineTransform)anAffineTransform { if (CGAffineTransformEqualToTransform(_affineTransform, anAffineTransform)) @@ -303,17 +304,17 @@ var CALayerRegisteredRunLoopUpdates = nil; } /*! - Returns the layer's affine transform. - */ + Returns the layer's affine transform. +*/ - (CGAffineTransform)affineTransform { return _affineTransform; } /*! - Sets the affine transform that gets applied to all the sublayers. - @param anAffineTransform the transform to apply to sublayers - */ + Sets the affine transform that gets applied to all the sublayers. + @param anAffineTransform the transform to apply to sublayers +*/ - (void)setSublayerTransform:(CGAffineTransform)anAffineTransform { if (CGAffineTransformEqualToTransform(_sublayerTransform, anAffineTransform)) @@ -337,39 +338,39 @@ var CALayerRegisteredRunLoopUpdates = nil; } /*! - Returns the affine transform applied to the sublayers. - */ + Returns the affine transform applied to the sublayers. +*/ - (CGAffineTransform)sublayerTransform { return _sublayerTransform; } /* - Private - @ignore - */ + Private + @ignore +*/ - (CGAffineTransform)transformToLayer { return _transformToLayer; } /*! - Sets the frame of the layer. The frame defines a bounding - rectangle in the superlayer's coordinate system. - @param aFrame the new frame rectangle - */ + Sets the frame of the layer. The frame defines a bounding + rectangle in the superlayer's coordinate system. + @param aFrame the new frame rectangle +*/ - (void)setFrame:(CGRect)aFrame { // FIXME: implement this } /*! - Returns the layer's frame. + Returns the layer's frame. - 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. - */ + 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) @@ -379,22 +380,22 @@ var CALayerRegisteredRunLoopUpdates = nil; } /*! - 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. - @return the backing store 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. + @return the backing store frame +*/ - (CGRect)backingStoreFrame { return _backingStoreFrame; } /*! - Sets the frame's backing store. - @param aFrame the new backing store. - */ + Sets the frame's backing store. + @param aFrame the new backing store. +*/ - (void)setBackingStoreFrame:(CGRect)aFrame { _hasCustomBackingStoreFrame = (aFrame != nil); @@ -408,7 +409,7 @@ var CALayerRegisteredRunLoopUpdates = nil; aFrame = [_superlayer convertRect:aFrame toLayer:nil]; var bounds = [_superlayer bounds], - frame = [_superlayer convertRect:bounds toLayer:nil]; + frame = [_superlayer convertRect:bounds toLayer:nil]; aFrame.origin.x -= CGRectGetMinX(frame); aFrame.origin.y -= CGRectGetMinY(frame); @@ -428,18 +429,18 @@ var CALayerRegisteredRunLoopUpdates = nil; // Providing Layer Content /*! - Returns the CGImage contents of this layer. - The default contents are \c nil. - */ + Returns the CGImage contents of this layer. + The default contents are \c nil. +*/ - (CGImage)contents { return _contents; } /*! - Sets the image contents of this layer. - @param contents the image to display - */ + Sets the image contents of this layer. + @param contents the image to display +*/ - (void)setContents:(CGImage)contents { if (_contents == contents) @@ -451,13 +452,12 @@ var CALayerRegisteredRunLoopUpdates = nil; } /* - Composites this layer onto the super layer, and draws its contents as well. - @ignore - */ + Composites this layer onto the super layer, and draws its contents as well. + @ignore +*/ - (void)composite { - // IMPROVEMENT: Added explicit parentheses for logical clarity in operator precedence. - if ((USE_BUFFER && !_contents) || !_context) + if (USE_BUFFER && !_contents || !_context) return; CGContextClearRect(_context, CGRectMake(0.0, 0.0, CGRectGetWidth(_backingStoreFrame), CGRectGetHeight(_backingStoreFrame))); @@ -468,7 +468,7 @@ var CALayerRegisteredRunLoopUpdates = nil; if (_superlayer) { var superlayerTransform = _CALayerGetTransform(_superlayer, nil), - superlayerOrigin = CGPointApplyAffineTransform(_superlayer._bounds.origin, superlayerTransform); + superlayerOrigin = CGPointApplyAffineTransform(_superlayer._bounds.origin, superlayerTransform); transform = CGAffineTransformConcat(_transformFromLayer, superlayerTransform); @@ -489,7 +489,7 @@ var CALayerRegisteredRunLoopUpdates = nil; if (USE_BUFFER) { - // CGContextDrawImage(_context, _bounds, _contents.context); +// CGContextDrawImage(_context, _bounds, _contents.context); _context.drawImage(_contents.buffer, CGRectGetMinX(_bounds), CGRectGetMinY(_bounds));//, CGRectGetWidth(_standardBackingStoreFrame), CGRectGetHeight(_standardBackingStoreFrame)); } else @@ -499,8 +499,8 @@ var CALayerRegisteredRunLoopUpdates = nil; } /*! - Displays the contents of this layer. - */ + Displays the contents of this layer. +*/ - (void)display { #if PLATFORM(DOM) @@ -548,9 +548,9 @@ var CALayerRegisteredRunLoopUpdates = nil; } /*! - Draws this layer's contents into the specified context. - @param aContext the context to draw the layer into - */ + Draws this layer's contents into the specified context. + @param aContext the context to draw the layer into +*/ - (void)drawInContext:(CGContext)aContext { if (_backgroundColor) @@ -566,18 +566,18 @@ var CALayerRegisteredRunLoopUpdates = nil; // Style Attributes /*! - Returns the opacity of the layer. The value is between - \c 0.0 (transparent) and \c 1.0 (opaque). - */ + Returns the opacity of the layer. The value is between + \c 0.0 (transparent) and \c 1.0 (opaque). +*/ - (float)opacity { return _opacity; } /*! - Sets the opacity for the layer. - @param anOpacity the new opacity (between \c 0.0 (transparent) and \c 1.0 (opaque)). - */ + Sets the opacity for the layer. + @param anOpacity the new opacity (between \c 0.0 (transparent) and \c 1.0 (opaque)). +*/ - (void)setOpacity:(float)anOpacity { if (_opacity == anOpacity) @@ -590,9 +590,9 @@ var CALayerRegisteredRunLoopUpdates = nil; } /*! - Sets whether the layer is hidden. - @param isHidden \c YES means the layer will be hidden. \c NO means the layer will be visible. - */ + Sets whether the layer is hidden. + @param isHidden \c YES means the layer will be hidden. \c NO means the layer will be visible. +*/ - (void)setHidden:(BOOL)isHidden { _isHidden = isHidden; @@ -600,25 +600,25 @@ var CALayerRegisteredRunLoopUpdates = nil; } /*! - Returns \c YES if the layer is hidden. - */ + Returns \c YES if the layer is hidden. +*/ - (BOOL)hidden { return _isHidden; } /*! - Returns \c YES if the layer is hidden. - */ + Returns \c YES if the layer is hidden. +*/ - (BOOL)isHidden { return _isHidden; } /*! - Sets whether content that goes lies outside the bounds is hidden or visible. - @param masksToBounds \c YES hides the excess content. \c NO makes it visible. - */ + Sets whether content that goes lies outside the bounds is hidden or visible. + @param masksToBounds \c YES hides the excess content. \c NO makes it visible. +*/ - (void)setMasksToBounds:(BOOL)masksToBounds { if (_masksToBounds == masksToBounds) @@ -629,9 +629,9 @@ var CALayerRegisteredRunLoopUpdates = nil; } /*! - Sets the layer's background color. - @param aColor the new background color - */ + Sets the layer's background color. + @param aColor the new background color +*/ - (void)setBackgroundColor:(CPColor)aColor { _backgroundColor = aColor; @@ -640,8 +640,8 @@ var CALayerRegisteredRunLoopUpdates = nil; } /*! - Returns the layer's background color. - */ + Returns the layer's background color. +*/ - (CPColor)backgroundColor { return _backgroundColor; @@ -649,34 +649,36 @@ var CALayerRegisteredRunLoopUpdates = nil; // Managing Layer Hierarchy /*! - Returns an array of the receiver's sublayers. - */ + Returns an array of the receiver's sublayers. +*/ - (CPArray)sublayers { return _sublayers; } /*! - Returns the receiver's superlayer. - */ + Returns the receiver's superlayer. +*/ - (CALayer)superlayer { return _superlayer; } -// IMPROVEMENT: Removed ADJUST_CONTENTS_ZINDEX macro in favor of inline replacement to eliminate preprocessor dependency. +#define ADJUST_CONTENTS_ZINDEX(aLayer)\ +if (_DOMContentsElement && aLayer._zPosition > _DOMContentsElement.style.zIndex)\ + _DOMContentsElement.style.zIndex -= 100.0;\ /*! - Adds the specified layer as a sublayer of the receiver. - */ + Adds the specified layer as a sublayer of the receiver. +*/ - (void)addSublayer:(CALayer)aLayer { [self insertSublayer:aLayer atIndex:_sublayers.length]; } /*! - Removes the receiver from its superlayer. - */ + Removes the receiver from its superlayer. +*/ - (void)removeFromSuperlayer { if (_owningView) @@ -692,10 +694,10 @@ var CALayerRegisteredRunLoopUpdates = nil; } /*! - Inserts the specified layer as a sublayer into the specified index. - @param aLayer the layer to insert - @param anIndex the index to insert the layer at - */ + Inserts the specified layer as a sublayer into the specified index. + @param aLayer the layer to insert + @param anIndex the index to insert the layer at +*/ - (void)insertSublayer:(CALayer)aLayer atIndex:(CPUInteger)anIndex { if (!aLayer) @@ -718,19 +720,15 @@ var CALayerRegisteredRunLoopUpdates = nil; else if (superlayer != nil) [aLayer removeFromSuperlayer]; - // IMPROVEMENT: Inlined ADJUST_CONTENTS_ZINDEX logic. - if (_DOMContentsElement && aLayer._zPosition > _DOMContentsElement.style.zIndex) - _DOMContentsElement.style.zIndex -= 100.0; + ADJUST_CONTENTS_ZINDEX(aLayer); [_sublayers insertObject:aLayer atIndex:anIndex]; #if PLATFORM(DOM) if (anIndex >= _sublayers.length - 1) - // IMPROVEMENT: Inlined DOM element property access. - _DOMElement.appendChild(aLayer._DOMElement); + _DOMElement.appendChild(DOM(aLayer)); else - // IMPROVEMENT: Inlined DOM element property access. - _DOMElement.insertBefore(aLayer._DOMElement, _sublayers[anIndex + 1]._DOMElement); + _DOMElement.insertBefore(DOM(aLayer), _sublayers[anIndex + 1]._DOMElement); #endif aLayer._superlayer = self; @@ -740,11 +738,11 @@ var CALayerRegisteredRunLoopUpdates = nil; } /*! - Inserts a layer below another layer. - @param aLayer the layer to insert - @param aSublayer the layer to insert below - @throws CALayerNotFoundException if \c aSublayer is not in the array of sublayers - */ + Inserts a layer below another layer. + @param aLayer the layer to insert + @param aSublayer the layer to insert below + @throws CALayerNotFoundException if \c aSublayer is not in the array of sublayers +*/ - (void)insertSublayer:(CALayer)aLayer below:(CALayer)aSublayer { var index = aSublayer ? [_sublayers indexOfObjectIdenticalTo:aSublayer] : 0; @@ -753,11 +751,11 @@ var CALayerRegisteredRunLoopUpdates = nil; } /*! - Inserts a layer above another layer. - @param aLayer the layer to insert - @param aSublayer the layer to insert above - @throws CALayerNotFoundException if \c aSublayer is not in the array of sublayers - */ + Inserts a layer above another layer. + @param aLayer the layer to insert + @param aSublayer the layer to insert above + @throws CALayerNotFoundException if \c aSublayer is not in the array of sublayers +*/ - (void)insertSublayer:(CALayer)aLayer above:(CALayer)aSublayer { var index = aSublayer ? [_sublayers indexOfObjectIdenticalTo:aSublayer] : _sublayers.length; @@ -769,10 +767,10 @@ var CALayerRegisteredRunLoopUpdates = nil; } /*! - Replaces a sublayer. - @param aSublayer the layer to insert - @param aLayer the layer to replace - */ + Replaces a sublayer. + @param aSublayer the layer to insert + @param aLayer the layer to replace +*/ - (void)replaceSublayer:(CALayer)aSublayer with:(CALayer)aLayer { if (aSublayer == aLayer) @@ -784,27 +782,23 @@ var CALayerRegisteredRunLoopUpdates = nil; return; } - // IMPROVEMENT: Inlined ADJUST_CONTENTS_ZINDEX logic. - if (_DOMContentsElement && aLayer._zPosition > _DOMContentsElement.style.zIndex) - _DOMContentsElement.style.zIndex -= 100.0; + ADJUST_CONTENTS_ZINDEX(aLayer); [_sublayers replaceObjectAtIndex:[_sublayers indexOfObjectIdenticalTo:aSublayer] withObject:aLayer]; - - // IMPROVEMENT: Inlined DOM element property access. - _DOMElement.replaceChild(aLayer._DOMElement, aSublayer._DOMElement); + _DOMElement.replaceChild(DOM(aSublayer), DOM(aLayer)); } // Updating Layer Display /* - Updates the layers on screen. - @ignore - */ + Updates the layers on screen. + @ignore +*/ + (void)runLoopUpdateLayers { for (UID in CALayerRegisteredRunLoopUpdates) { var layer = CALayerRegisteredRunLoopUpdates[UID], - mask = layer._runLoopUpdateMask; + mask = layer._runLoopUpdateMask; if (mask & CALayerDOMUpdateMask) _CALayerUpdateDOM(layer, mask); @@ -822,8 +816,8 @@ var CALayerRegisteredRunLoopUpdates = nil; } /* - @ignore - */ + @ignore +*/ - (void)registerRunLoopUpdateWithMask:(unsigned)anUpdateMask { if (CALayerRegisteredRunLoopUpdates == nil) @@ -831,7 +825,7 @@ var CALayerRegisteredRunLoopUpdates = nil; CALayerRegisteredRunLoopUpdates = {}; [[CPRunLoop currentRunLoop] performSelector:@selector(runLoopUpdateLayers) - target:CALayer argument:nil order:0 modes:[CPDefaultRunLoopMode]]; + target:CALayer argument:nil order:0 modes:[CPDefaultRunLoopMode]]; } _runLoopUpdateMask |= anUpdateMask; @@ -839,88 +833,88 @@ var CALayerRegisteredRunLoopUpdates = nil; } /* - @ignore - */ + @ignore +*/ - (void)setNeedsComposite { [self registerRunLoopUpdateWithMask:CALayerCompositeUpdateMask]; } /*! - Marks the layer as needing to be redrawn. - */ + Marks the layer as needing to be redrawn. +*/ - (void)setNeedsDisplay { [self registerRunLoopUpdateWithMask:CALayerDisplayUpdateMask]; } /*! - Sets whether the layer needs to be redrawn when its bounds are changed. - @param needsDisplayOnBoundsChange \c YES means the display is redraw on a bounds change. - */ + Sets whether the layer needs to be redrawn when its bounds are changed. + @param needsDisplayOnBoundsChange \c YES means the display is redraw on a bounds change. +*/ - (void)setNeedsDisplayOnBoundsChange:(BOOL)needsDisplayOnBoundsChange { _needsDisplayOnBoundsChange = needsDisplayOnBoundsChange; } /*! - Returns \c YES if the display should be redrawn on a bounds change. - */ + Returns \c YES if the display should be redrawn on a bounds change. +*/ - (BOOL)needsDisplayOnBoundsChange { return _needsDisplayOnBoundsChange; } /*! - Marks the specified rectangle as needing to be redrawn. - @param aRect the area that needs to be redrawn. - */ + Marks the specified rectangle as needing to be redrawn. + @param aRect the area that needs to be redrawn. +*/ - (void)setNeedsDisplayInRect:(CGRect)aRect { - // _dirtyRect = aRect; +// _dirtyRect = aRect; [self display]; } // Mapping Between Coordinate and Time Spaces /*! - Converts the point from the specified layer's coordinate system into the receiver's coordinate system. - @param aPoint the point to convert - @param aLayer the layer coordinate system to convert from - @return the converted point - */ + Converts the point from the specified layer's coordinate system into the receiver's coordinate system. + @param aPoint the point to convert + @param aLayer the layer coordinate system to convert from + @return the converted point +*/ - (CGPoint)convertPoint:(CGPoint)aPoint fromLayer:(CALayer)aLayer { return CGPointApplyAffineTransform(aPoint, _CALayerGetTransform(aLayer, self)); } /*! - Converts the point from the receiver's coordinate system to the specified layer's coordinate system. - @param aPoint the point to convert - @param aLayer the layer coordinate system to convert to - @return the converted point - */ + Converts the point from the receiver's coordinate system to the specified layer's coordinate system. + @param aPoint the point to convert + @param aLayer the layer coordinate system to convert to + @return the converted point +*/ - (CGPoint)convertPoint:(CGPoint)aPoint toLayer:(CALayer)aLayer { return CGPointApplyAffineTransform(aPoint, _CALayerGetTransform(self, aLayer)); } /*! - Converts the rectangle from the specified layer's coordinate system to the receiver's coordinate system. - @param aRect the rectangle to convert - @param aLayer the layer coordinate system to convert from - @return the converted rectangle - */ + Converts the rectangle from the specified layer's coordinate system to the receiver's coordinate system. + @param aRect the rectangle to convert + @param aLayer the layer coordinate system to convert from + @return the converted rectangle +*/ - (CGRect)convertRect:(CGRect)aRect fromLayer:(CALayer)aLayer { return CGRectApplyAffineTransform(aRect, _CALayerGetTransform(aLayer, self)); } /*! - Converts the rectangle from the receiver's coordinate system to the specified layer's coordinate system. - @param aRect the rectangle to convert - @param aLayer the layer coordinate system to convert to - @return the converted rectangle - */ + Converts the rectangle from the receiver's coordinate system to the specified layer's coordinate system. + @param aRect the rectangle to convert + @param aLayer the layer coordinate system to convert to + @return the converted rectangle +*/ - (CGRect)convertRect:(CGRect)aRect toLayer:(CALayer)aLayer { return CGRectApplyAffineTransform(aRect, _CALayerGetTransform(self, aLayer)); @@ -928,19 +922,19 @@ var CALayerRegisteredRunLoopUpdates = nil; // Hit Testing /*! - Returns \c YES if the layer contains the point. - @param aPoint the point to test - */ + Returns \c YES if the layer contains the point. + @param aPoint the point to test +*/ - (BOOL)containsPoint:(CGPoint)aPoint { return CGRectContainsPoint(_bounds, aPoint); } /*! - Returns the farthest descendant of this layer that contains the specified point. - @param aPoint the point to test - @return the containing layer or \c nil if there was no hit. - */ + Returns the farthest descendant of this layer that contains the specified point. + @param aPoint the point to test + @return the containing layer or \c nil if there was no hit. +*/ - (CALayer)hitTest:(CGPoint)aPoint { if (_isHidden) @@ -952,7 +946,7 @@ var CALayerRegisteredRunLoopUpdates = nil; return nil; var layer = nil, - index = _sublayers.length; + index = _sublayers.length; // FIXME: this should take into account zPosition. while (index--) @@ -964,9 +958,9 @@ var CALayerRegisteredRunLoopUpdates = nil; // Modifying the Delegate /*! - Sets the delegate for this layer. - @param aDelegate the delegate - */ + Sets the delegate for this layer. + @param aDelegate the delegate +*/ - (void)setDelegate:(id)aDelegate { if (_delegate == aDelegate) @@ -982,19 +976,19 @@ var CALayerRegisteredRunLoopUpdates = nil; } /*! - Returns the layer's delegate - */ + Returns the layer's delegate +*/ - (id)delegate { return _delegate; } /* - Adds an animation to the layer. - Supports CABasicAnimation for Numbers (opacity) and Points (position/anchorPoint). - The animation is exerted by means of periodically applying the keypath on the delegate - Only works if the delegate is set! - */ + Adds an animation to the layer. + Supports CABasicAnimation for Numbers (opacity) and Points (position/anchorPoint). + The animation is exerted by means of periodically applying the keypath on the delegate + Only works if the delegate is set! +*/ - (void)addAnimation:(CAAnimation)anim forKey:(CPString)key { if (!anim) return; @@ -1004,24 +998,24 @@ var CALayerRegisteredRunLoopUpdates = nil; if ([anim respondsToSelector:@selector(animations)] && [anim animations]) { var animations = [anim animations], - count = [animations count], - i = 0; + count = [animations count], + i = 0; for (; i < count; i++) { var child = [animations objectAtIndex:i]; - - // Recurse: Add the child animation. - // We pass 'nil' for the key so the child's own 'keyPath' + + // Recurse: Add the child animation. + // We pass 'nil' for the key so the child's own 'keyPath' // is used as the storage identifier in the dictionary. [self addAnimation:child forKey:nil]; } - return; + return; } // --- 2. Determine KeyPath --- var keyPath = key; - + // If the animation object has an explicit keyPath (like CABasicAnimation), use it. if ([anim respondsToSelector:@selector(keyPath)] && [anim keyPath]) keyPath = [anim keyPath]; @@ -1051,7 +1045,7 @@ var CALayerRegisteredRunLoopUpdates = nil; return; var duration = ([anim respondsToSelector:@selector(duration)]) ? [anim duration] : 0.25; - + // Default to EaseInEaseOut if not specified var timingFunction = ([anim respondsToSelector:@selector(timingFunction)]) ? [anim timingFunction] : [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; @@ -1068,7 +1062,7 @@ var CALayerRegisteredRunLoopUpdates = nil; }; // --- 5. Render Loop --- - var _self = self; + var _self = self; var renderLoop = function(timestamp) { if ([_self _renderAnimationStep:context timestamp:timestamp]) @@ -1102,33 +1096,33 @@ var CALayerRegisteredRunLoopUpdates = nil; - (void)removeAllAnimations { var keys = [_activeAnimations allKeys], - count = [keys count]; + count = [keys count]; while (count--) [self removeAnimationForKey:[keys objectAtIndex:count]]; } /* - Solves Cubic Bezier for t. - p1, p2 are the control points (x,y). p0 is 0,0, p3 is 1,1. - This is a simplified solver for standard Core Animation timing functions. - */ + Solves Cubic Bezier for t. + p1, p2 are the control points (x,y). p0 is 0,0, p3 is 1,1. + This is a simplified solver for standard Core Animation timing functions. +*/ - (float)_solveBezier:(float)t forTimingFunction:(CAMediaTimingFunction)tf { if (!tf) return t; - + // Linear optimization - if (tf === [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]) + if (tf === [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]) return t; var points = [tf controlPoints]; // [c1x, c1y, c2x, c2y] - var p1x = points[0], p1y = points[1], - p2x = points[2], p2y = points[3]; + var p1x = points[0], p1y = points[1], + p2x = points[2], p2y = points[3]; // Simple polynomial evaluation (De Casteljau's algorithm/Cubic formula subset) - // Since we are usually dealing with standard easing, we can approximate 1D easing on the Y axis + // Since we are usually dealing with standard easing, we can approximate 1D easing on the Y axis // based on linear time X, or do a full solve. // For brevity/speed in JS, we often approximate basic easing: - + // 3t^2 * (1-t) + t^3 ... standard bezier blending functions var cx = 3.0 * p1x; var bx = 3.0 * (p2x - p1x) - cx; @@ -1158,7 +1152,7 @@ var CALayerRegisteredRunLoopUpdates = nil; context.startTime = timestamp; var elapsed = timestamp - context.startTime, - linearProgress = elapsed / context.duration; + linearProgress = elapsed / context.duration; if (linearProgress > 1.0) linearProgress = 1.0; @@ -1166,8 +1160,8 @@ var CALayerRegisteredRunLoopUpdates = nil; var progress = [self _solveBezier:linearProgress forTimingFunction:context.timingFunction]; var start = context.startValue, - end = context.endValue, - current = nil; + end = context.endValue, + current = nil; // Number if (typeof start === "number") @@ -1188,11 +1182,11 @@ var CALayerRegisteredRunLoopUpdates = nil; else if (start && start.origin !== undefined && start.size !== undefined) // CGRect { current = CGRectMake( - start.origin.x + (end.origin.x - start.origin.x) * progress, - start.origin.y + (end.origin.y - start.origin.y) * progress, - start.size.width + (end.size.width - start.size.width) * progress, - start.size.height + (end.size.height - start.size.height) * progress - ); + start.origin.x + (end.origin.x - start.origin.x) * progress, + start.origin.y + (end.origin.y - start.origin.y) * progress, + start.size.width + (end.size.width - start.size.width) * progress, + start.size.height + (end.size.height - start.size.height) * progress + ); } if (current !== nil) @@ -1201,10 +1195,10 @@ var CALayerRegisteredRunLoopUpdates = nil; if (linearProgress >= 1.0) { var anim = context.animation; - + // Cleanup var shouldRemove = [anim respondsToSelector:@selector(isRemovedOnCompletion)] ? [anim isRemovedOnCompletion] : YES; - + if (shouldRemove) { // Find key by context identity to handle groups correctly var keys = [_activeAnimations allKeys]; @@ -1278,14 +1272,14 @@ var CALayerRegisteredRunLoopUpdates = nil; function _CALayerUpdateSublayerTransformForSublayers(aLayer) { var bounds = aLayer._bounds, - anchorPoint = aLayer._anchorPoint, - translateX = CGRectGetWidth(bounds) * anchorPoint.x, - translateY = CGRectGetHeight(bounds) * anchorPoint.y; + 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))); + CGAffineTransformMakeTranslation(-translateX, -translateY), + CGAffineTransformConcat(aLayer._sublayerTransform, + CGAffineTransformMakeTranslation(translateX, translateY))); } function _CALayerUpdateDOM(aLayer, aMask) @@ -1307,8 +1301,8 @@ function _CALayerUpdateDOM(aLayer, aMask) if (aMask & CALayerFrameSizeUpdateMask) { var width = MAX(0.0, ROUND(CGRectGetWidth(frame))), - height = MAX(0.0, ROUND(CGRectGetHeight(frame))), - DOMContentsElement = aLayer._DOMContentsElement; + height = MAX(0.0, ROUND(CGRectGetHeight(frame))), + DOMContentsElement = aLayer._DOMContentsElement; DOMElementStyle.width = width + "px"; DOMElementStyle.height = height + "px"; @@ -1327,20 +1321,20 @@ function _CALayerUpdateDOM(aLayer, aMask) 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; + 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))); + CGAffineTransformMakeTranslation(-width * anchorPoint.x - CGRectGetMinX(aLayer._bounds), -height * anchorPoint.y - CGRectGetMinY(aLayer._bounds)), + CGAffineTransformConcat(affineTransform, + CGAffineTransformMakeTranslation(position.x, position.y))); if (superlayer && superlayer._hasSublayerTransform) { @@ -1358,9 +1352,8 @@ function _CALayerRecalculateGeometry(aLayer, aGeometryChange) if (superlayer) { - // IMPROVEMENT: Renamed local 'bounds' to 'superlayerBounds' to prevent shadowing of outer 'bounds' declaration. - var superlayerBounds = [superlayer bounds], - frame = [superlayer convertRect:superlayerBounds toLayer:nil]; + var bounds = [superlayer bounds], + frame = [superlayer convertRect:bounds toLayer:nil]; aLayer._standardBackingStoreFrame.origin.x -= CGRectGetMinX(frame); aLayer._standardBackingStoreFrame.origin.y -= CGRectGetMinY(frame); @@ -1372,7 +1365,7 @@ function _CALayerRecalculateGeometry(aLayer, aGeometryChange) // bigger than the "optimal" bounding integral rect since that doesn't change drawing. var origin = aLayer._standardBackingStoreFrame.origin, - size = aLayer._standardBackingStoreFrame.size; + size = aLayer._standardBackingStoreFrame.size; origin.x = FLOOR(origin.x); origin.y = FLOOR(origin.y); @@ -1395,7 +1388,7 @@ function _CALayerRecalculateGeometry(aLayer, aGeometryChange) // 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)))) + CGRectGetHeight(backingStoreFrame) != ROUND(CGRectGetHeight(aLayer._backingStoreFrame)))) [aLayer registerRunLoopUpdateWithMask:CALayerFrameSizeUpdateMask]; aLayer._backingStoreFrame = backingStoreFrame; @@ -1411,8 +1404,8 @@ function _CALayerRecalculateGeometry(aLayer, aGeometryChange) [aLayer setNeedsComposite]; var sublayers = aLayer._sublayers, - index = 0, - count = sublayers.length; + index = 0, + count = sublayers.length; for (; index < count; ++index) _CALayerRecalculateGeometry(sublayers[index], aGeometryChange); @@ -1444,7 +1437,7 @@ function _CALayerGetTransform(fromLayer, toLayer) } var layers = [], - layer = toLayer; + layer = toLayer; while (layer) {