CPPopover fixes/enhancements

- Lion-style popovers now zoom open just like their Lion counterparts (except for opacity, the browser couldn't handle that at the same time).
- Fixed a number of subtle drawing issues caused by not aligning path points correctly.
- The anchor arrow deals correctly with corner cases where the middle of the reference rect is not within the corners of the popover.
- Code cleanup: factored redundant code, removed unused code.
- Added corner cases to the demo app, changed demo app to reuse a single popover.
This commit is contained in:
Aparajita Fishman
2012-03-23 19:53:46 -07:00
parent 28ed2f9bd0
commit 1a8fc7f07f
3 changed files with 381 additions and 222 deletions
+187 -99
View File
@@ -22,11 +22,15 @@
@import "_CPWindowView.j"
#define ALIGN_STROKE(point) (FLOOR(point) === (point) ? (point) + halfStrokeWidth : (point))
#define ALIGN_COORD(point) (FLOOR(point))
var _CPAttachedWindowViewDefaultCursorSize = CGSizeMake(16, 10);
/*!
@ignore
A custom CPWindowView that manage border and cursor
A custom CPWindowView that manages a border and cursor
*/
@implementation _CPAttachedWindowView : _CPWindowView
{
@@ -36,7 +40,7 @@
int _appearance @accessors(property=appearance);
unsigned _preferredEdge @accessors(property=preferredEdge);
CPSize _cursorSize;
CGSize _cursorSize;
}
/*!
@@ -81,18 +85,16 @@
/*!
Initialize the _CPWindowView
*/
- (id)initWithFrame:(CPRect)aFrame styleMask:(unsigned)aStyleMask
- (id)initWithFrame:(CGRect)aFrame styleMask:(unsigned)aStyleMask
{
if (self = [super initWithFrame:aFrame styleMask:aStyleMask])
{
var bundle = [CPBundle bundleForClass:[self class]];
_arrowOffsetX = 0.0;
_arrowOffsetY = 0.0;
// @TODO: make this themable
_useGlowingEffect = YES;
_appearance = CPPopoverAppearanceMinimal;
_cursorSize = CPSizeMake(15, 10);
_cursorSize = CGSizeMakeCopy(_CPAttachedWindowViewDefaultCursorSize);
}
return self;
@@ -103,7 +105,7 @@
*/
- (void)hideCursor
{
_cursorSize = CPSizeMakeZero();
_cursorSize = CGSizeMakeZero();
[self setNeedsDisplay:YES];
}
@@ -112,7 +114,7 @@
*/
- (void)showCursor
{
_cursorSize = CPSizeMake(15, 10);
_cursorSize = CGSizeMakeCopy(_CPAttachedWindowViewDefaultCursorSize);
[self setNeedsDisplay:YES];
_mouseDownPressed = NO;
}
@@ -129,11 +131,13 @@
arrowWidth = _cursorSize.width,
arrowHeight = _cursorSize.height,
strokeWidth = 1,
halfStrokeWidth = strokeWidth / 2.0,
strokeColor,
shadowColor = [[CPColor blackColor] colorWithAlphaComponent:.2],
shadowSize = CGSizeMake(0, 7),
shadowSize = CGSizeMake(0, 6),
shadowBlur = 15,
gradient;
gradient,
frame = [self bounds];
if (_appearance == CPPopoverAppearanceMinimal)
{
@@ -149,133 +153,217 @@
}
// fix rect to take care of stroke and shadow
aRect.origin.x += strokeWidth + shadowBlur;
aRect.origin.y += strokeWidth + (shadowBlur + shadowSize.height / 2);
aRect.size.width -= (strokeWidth * 2) + (shadowBlur * 2);
aRect.size.height -= (strokeWidth * 2) + (shadowBlur * 2 + shadowSize.height);
frame.origin.x += halfStrokeWidth + shadowBlur;
frame.origin.y += halfStrokeWidth + (shadowBlur + shadowSize.height / 2);
frame.size.width -= strokeWidth + (shadowBlur * 2);
frame.size.height -= strokeWidth + (shadowBlur * 2 + shadowSize.height);
CGContextSetStrokeColor(context, strokeColor);
CGContextSetLineWidth(context, strokeWidth);
CGContextBeginPath(context);
CGContextSetShadowWithColor(context, shadowSize, shadowBlur, shadowColor);
CGContextDrawLinearGradient(context, gradient, CGPointMake(CPRectGetMidX(aRect), 0.0), CGPointMake(CPRectGetMidX(aRect), aRect.size.height), 0);
CGContextDrawLinearGradient(context, gradient, CGPointMake(CGRectGetMidX(frame), 0.0), CGPointMake(CGRectGetMidX(frame), frame.size.height), 0);
var xMin = _CGRectGetMinX(aRect),
xMax = _CGRectGetMaxX(aRect),
yMin = _CGRectGetMinY(aRect),
yMax = _CGRectGetMaxY(aRect);
var xMin = _CGRectGetMinX(frame),
xMax = _CGRectGetMaxX(frame),
yMin = _CGRectGetMinY(frame),
yMax = _CGRectGetMaxY(frame),
arrowMinX = ALIGN_COORD(xMin + radius + strokeWidth),
arrowMaxX = ALIGN_COORD(xMax - radius - strokeWidth),
arrowMinY = ALIGN_COORD(yMin + radius + strokeWidth),
arrowMaxY = ALIGN_COORD(yMax - radius + strokeWidth),
arrowAnchor = CGPointMakeZero(),
arrowStart = CGPointMakeZero(),
pt = CGPointMakeZero();
// draw!
switch (_preferredEdge)
{
case CPMinXEdge:
// origin ne
CGContextMoveToPoint(context, xMin + radius, yMin);
// ne
CGContextAddLineToPoint(context, xMax - radius, yMin);
CGContextAddCurveToPoint(context, xMax - radius, yMin, xMax, yMin, xMax, yMin + radius);
// arrow CPMinXEdge
CGContextAddLineToPoint(context, xMax, (aRect.size.height / 2) + aRect.origin.y + _arrowOffsetY - (arrowHeight - 2));
CGContextAddLineToPoint(context, aRect.size.width + arrowHeight + aRect.origin.x + _arrowOffsetX, (aRect.size.height / 2) + aRect.origin.y + _arrowOffsetY);
CGContextAddLineToPoint(context, aRect.size.width + aRect.origin.x + _arrowOffsetX, (aRect.size.height / 2 + (arrowWidth / 2)) + aRect.origin.y + _arrowOffsetY);
// se
CGContextAddLineToPoint(context, xMax, yMax - radius);
CGContextAddCurveToPoint(context, xMax, yMax - radius, xMax, yMax, xMax - radius, yMax);
// sw
CGContextAddLineToPoint(context, xMin + radius, yMax);
CGContextAddCurveToPoint(context, xMin + radius, yMax, xMin, yMax, xMin, yMax - radius);
// nw
CGContextAddLineToPoint(context, xMin, yMin + radius);
CGContextAddCurveToPoint(context, xMin, yMin + radius, xMin, yMin, xMin + radius, yMin);
break;
case CPMaxXEdge:
// origin ne
CGContextMoveToPoint(context, xMin + radius, yMin);
// origin nw
pt.x = ALIGN_COORD(xMin + radius);
pt.y = yMin;
CGContextMoveToPoint(context, pt.x, pt.y);
// ne
CGContextAddLineToPoint(context, xMax - radius, yMin);
CGContextAddCurveToPoint(context, xMax - radius, yMin, xMax, yMin, xMax, yMin + radius);
pt.x = ALIGN_COORD(xMax - radius);
CGContextAddLineToPoint(context, pt.x, pt.y);
CGContextAddCurveToPoint(context, pt.x, pt.y, xMax, yMin, xMax, ALIGN_COORD(yMin + radius));
if (_preferredEdge === CPMinXEdge)
{
// arrow CPMinXEdge
arrowAnchor.x = ALIGN_STROKE(xMax);
arrowAnchor.y = ALIGN_COORD((frame.size.height / 2) + yMin + _arrowOffsetY);
// to top edge
pt.y = ALIGN_COORD(arrowAnchor.y - (arrowWidth / 2));
// adjust starting point to not go beyond the corner
if (pt.y <= arrowMinY)
pt.y = arrowMinY;
else if ((pt.y + arrowWidth) > arrowMaxY)
pt.y = arrowMaxY - arrowWidth;
pt.x = arrowAnchor.x;
arrowStart = CGPointMakeCopy(pt);
CGContextAddLineToPoint(context, pt.x, pt.y);
// top edge -> point
pt.x = ALIGN_STROKE(arrowAnchor.x + arrowHeight);
pt.y = arrowAnchor.y;
CGContextAddLineToPoint(context, pt.x, pt.y);
// point -> bottom edge
pt.x = arrowAnchor.x;
pt.y = ALIGN_COORD(arrowStart.y + arrowWidth);
CGContextAddLineToPoint(context, pt.x, pt.y);
}
// se
CGContextAddLineToPoint(context, xMax, yMax - radius);
CGContextAddCurveToPoint(context, xMax, yMax - radius, xMax, yMax, xMax - radius, yMax);
pt.x = xMax;
pt.y = ALIGN_COORD(yMax - radius);
CGContextAddLineToPoint(context, pt.x, pt.y);
CGContextAddCurveToPoint(context, pt.x, pt.y, pt.x, yMax, ALIGN_COORD(xMax - radius), yMax);
// sw
CGContextAddLineToPoint(context, xMin + radius, yMax);
CGContextAddCurveToPoint(context, xMin + radius, yMax, xMin, yMax, xMin, yMax - radius);
pt.x = ALIGN_COORD(xMin + radius);
pt.y = yMax;
CGContextAddLineToPoint(context, pt.x, pt.y);
CGContextAddCurveToPoint(context, pt.x, pt.y, xMin, pt.y, xMin, ALIGN_COORD(yMax - radius));
// arrow CPMaxXEdge
CGContextAddLineToPoint(context, xMin, (aRect.size.height / 2 + (arrowWidth / 2) + aRect.origin.y + _arrowOffsetY));
CGContextAddLineToPoint(context, aRect.origin.x - arrowHeight + _arrowOffsetX, (aRect.size.height / 2) + aRect.origin.y + _arrowOffsetY);
CGContextAddLineToPoint(context, aRect.origin.x + _arrowOffsetX, (aRect.size.height / 2 - (arrowWidth / 2) + aRect.origin.y + _arrowOffsetY));
if (_preferredEdge === CPMaxXEdge)
{
// arrow CPMaxXEdge
arrowAnchor.x = ALIGN_STROKE(xMin);
arrowAnchor.y = ALIGN_COORD((frame.size.height / 2) + yMin + _arrowOffsetY);
// to bottom edge
pt.y = ALIGN_COORD(arrowAnchor.y + (arrowWidth / 2));
// adjust starting point to not go beyond the corner
if ((pt.y - arrowWidth) < arrowMinY)
pt.y = arrowMinY + arrowWidth;
else if (pt.y > arrowMaxY)
pt.y = arrowMaxY;
pt.x = arrowAnchor.x;
arrowStart = CGPointMakeCopy(pt);
CGContextAddLineToPoint(context, pt.x, pt.y);
// bottom edge -> point
pt.x = ALIGN_STROKE(arrowAnchor.x - arrowHeight);
pt.y = arrowAnchor.y;
CGContextAddLineToPoint(context, pt.x, pt.y);
// point -> top edge
pt.x = arrowAnchor.x;
pt.y = ALIGN_COORD(arrowStart.y - arrowWidth);
CGContextAddLineToPoint(context, pt.x, pt.y);
}
// nw
CGContextAddLineToPoint(context, xMin, yMin + radius);
CGContextAddCurveToPoint(context, xMin, yMin + radius, xMin, yMin, xMin + radius, yMin);
pt.x = xMin;
pt.y = ALIGN_COORD(yMin + radius);
CGContextAddLineToPoint(context, pt.x, pt.y);
CGContextAddCurveToPoint(context, pt.x, pt.y, pt.x, yMin, ALIGN_COORD(xMin + radius), yMin);
break;
case CPMaxYEdge:
// origin nw
CGContextMoveToPoint(context, xMin, yMin + yMin);
// nw
CGContextAddLineToPoint(context, xMin, yMin + radius);
CGContextAddCurveToPoint(context, xMin, yMin + radius, xMin, yMin, xMin + radius, yMin);
// arrow CPMaxYEdge
CGContextAddLineToPoint(context, (aRect.size.width / 2) + aRect.origin.x + _arrowOffsetX - (arrowWidth / 2), yMin);
CGContextAddLineToPoint(context, (aRect.size.width / 2) + aRect.origin.x + _arrowOffsetX, aRect.origin.y - arrowHeight + _arrowOffsetY);
CGContextAddLineToPoint(context, (aRect.size.width / 2) + (arrowWidth / 2) + aRect.origin.x + _arrowOffsetX, aRect.origin.y + _arrowOffsetY);
// ne
CGContextAddLineToPoint(context, xMax - radius, yMin);
CGContextAddCurveToPoint(context, xMax - radius, yMin, xMax, yMin, xMax, yMin + radius);
// se
CGContextAddLineToPoint(context, xMax, yMax - radius);
CGContextAddCurveToPoint(context, xMax, yMax - radius, xMax, yMax, xMax - radius, yMax);
// sw
CGContextAddLineToPoint(context, xMin + radius, yMax);
CGContextAddCurveToPoint(context, xMin + radius, yMax, xMin, yMax, xMin, yMax - radius);
break;
case CPMinYEdge:
// origin nw
CGContextMoveToPoint(context, xMin, yMin + yMin);
// origin sw
pt.x = xMin;
pt.y = ALIGN_COORD(yMax - radius);
CGContextMoveToPoint(context, pt.x, pt.y);
// nw
CGContextAddLineToPoint(context, xMin, yMin + radius);
CGContextAddCurveToPoint(context, xMin, yMin + radius, xMin, yMin, xMin + radius, yMin);
pt.y = ALIGN_COORD(yMin + radius);
CGContextAddLineToPoint(context, pt.x, pt.y);
CGContextAddCurveToPoint(context, pt.x, pt.y, pt.x, yMin, ALIGN_COORD(xMin + radius), yMin);
if (_preferredEdge === CPMaxYEdge)
{
// arrow CPMaxYEdge
arrowAnchor.x = ALIGN_COORD((frame.size.width / 2) + xMin + _arrowOffsetX);
arrowAnchor.y = ALIGN_STROKE(yMin + _arrowOffsetY);
// to left edge
pt.x = ALIGN_COORD(arrowAnchor.x - (arrowWidth / 2));
// adjust starting point to not go beyond the corner
if (pt.x < arrowMinX)
pt.x = arrowMinX;
else if ((pt.x + arrowWidth) > arrowMaxX)
pt.x = arrowMaxX - arrowWidth;
pt.y = arrowAnchor.y;
arrowStart = CGPointMakeCopy(pt);
CGContextAddLineToPoint(context, pt.x, pt.y);
// left edge -> point
pt.x = arrowAnchor.x;
pt.y = ALIGN_STROKE(arrowAnchor.y - arrowHeight);
CGContextAddLineToPoint(context, pt.x, pt.y);
// point -> right edge
pt.x = ALIGN_COORD(arrowStart.x + arrowWidth);
pt.y = arrowAnchor.y;
CGContextAddLineToPoint(context, pt.x, pt.y);
}
// ne
CGContextAddLineToPoint(context, xMax - radius, yMin);
CGContextAddCurveToPoint(context, xMax - radius, yMin, xMax, yMin, xMax, yMin + radius);
pt.x = ALIGN_COORD(xMax - radius);
pt.y = yMin;
CGContextAddLineToPoint(context, pt.x, pt.y);
CGContextAddCurveToPoint(context, pt.x, pt.y, xMax, pt.y, xMax, ALIGN_COORD(yMin + radius));
// se
CGContextAddLineToPoint(context, xMax, yMax - radius);
CGContextAddCurveToPoint(context, xMax, yMax - radius, xMax, yMax, xMax - radius, yMax);
pt.x = xMax;
pt.y = ALIGN_COORD(yMax - radius);
CGContextAddLineToPoint(context, pt.x, pt.y);
CGContextAddCurveToPoint(context, pt.x, pt.y, pt.x, yMax, ALIGN_COORD(xMax - radius), yMax);
// arrow CPMinYEdge
CGContextAddLineToPoint(context, (aRect.size.width / 2) + (arrowWidth / 2) + aRect.origin.x + _arrowOffsetX , yMax);
CGContextAddLineToPoint(context, (aRect.size.width / 2) + aRect.origin.x + _arrowOffsetX, aRect.size.height + aRect.origin.y + arrowHeight + _arrowOffsetY);
CGContextAddLineToPoint(context, (aRect.size.width / 2) - (arrowWidth / 2) + aRect.origin.x + _arrowOffsetX, aRect.size.height + aRect.origin.y + _arrowOffsetY);
if (_preferredEdge === CPMinYEdge)
{
// arrow CPMinYEdge
arrowAnchor.x = ALIGN_COORD((frame.size.width / 2) + xMin + _arrowOffsetX);
arrowAnchor.y = ALIGN_STROKE(yMax + _arrowOffsetY);
// to right edge
pt.x = ALIGN_COORD(arrowAnchor.x + (arrowWidth / 2));
// adjust starting point to not go beyond the corner
if ((pt.x - arrowWidth) < arrowMinX)
pt.x = arrowMinX + arrowWidth;
else if (pt.x > arrowMaxX)
pt.x = arrowMaxX;
pt.y = arrowAnchor.y;
arrowStart = CGPointMakeCopy(pt);
CGContextAddLineToPoint(context, pt.x, pt.y);
// right edge -> point
pt.x = arrowAnchor.x;
pt.y = ALIGN_STROKE(arrowAnchor.y + arrowHeight);
CGContextAddLineToPoint(context, pt.x, pt.y);
// point -> left edge
pt.x = ALIGN_COORD(arrowStart.x - arrowWidth);
pt.y = arrowAnchor.y;
CGContextAddLineToPoint(context, pt.x, pt.y);
}
// sw
CGContextAddLineToPoint(context, xMin + radius, yMax);
CGContextAddCurveToPoint(context, xMin + radius, yMax, xMin, yMax, xMin, yMax - radius);
pt.x = ALIGN_COORD(xMin + radius);
pt.y = yMax;
CGContextAddLineToPoint(context, pt.x, pt.y);
CGContextAddCurveToPoint(context, pt.x, pt.y, xMin, pt.y, xMin, ALIGN_COORD(yMax - radius));
break;
default:
// no computed edge means standard rounded rect
CGContextAddPath(context, CGPathWithRoundedRectangleInRect(aRect, radius, radius, YES, YES, YES, YES));
CGContextAddPath(context, CGPathWithRoundedRectangleInRect(frame, radius, radius, YES, YES, YES, YES));
}
CGContextClosePath(context);
+94 -73
View File
@@ -49,9 +49,9 @@ var _CPAttachedWindow_attachedWindowShouldClose_ = 1 << 0,
BOOL _closeOnBlur;
BOOL _isClosed;
BOOL _browserAnimates;
BOOL _shouldPerformAnimation;
CPButton _closeButton;
float _animationDuration;
CPInteger _implementedDelegateMethods;
}
@@ -128,18 +128,16 @@ var _CPAttachedWindow_attachedWindowShouldClose_ = 1 << 0,
{
_animates = YES;
_animationStyle = CPPopoverAnimationStyleLion;
_animationDuration = 150;
_closeOnBlur = (aStyleMask & CPClosableOnBlurWindowMask);
_isClosed = NO;
_browserAnimates = [self browserSupportsAnimation];
_shouldPerformAnimation = _animates;
[self setLevel:CPStatusWindowLevel];
[self setMovableByWindowBackground:YES];
[self setHasShadow:NO];
_DOMElement.style.webkitBackfaceVisibility = "hidden";
_DOMElement.style.webkitTransitionProperty = "-webkit-transform, opacity";
_DOMElement.style.webkitTransitionDuration = _animationDuration + "ms";
[self setCSS3Property:@"TransitionProperty" value:@"-webkit-transform, opacity"];
[_windowView setNeedsDisplay:YES];
}
@@ -247,23 +245,24 @@ var _CPAttachedWindow_attachedWindowShouldClose_ = 1 << 0,
originLeft = CGPointCreateCopy(aRect.origin),
originRight = CGPointCreateCopy(aRect.origin),
originTop = CGPointCreateCopy(aRect.origin),
originBottom = CGPointCreateCopy(aRect.origin);
originBottom = CGPointCreateCopy(aRect.origin),
frameSize = [self frame].size;
// CPMaxXEdge
originRight.x += aRect.size.width;
originRight.y += (aRect.size.height / 2.0) - (CGRectGetHeight([self frame]) / 2.0);
originRight.y += (aRect.size.height / 2.0) - (frameSize.height / 2.0);
// CPMinXEdge
originLeft.x -= CGRectGetWidth([self frame]);
originLeft.y += (aRect.size.height / 2.0) - (CGRectGetHeight([self frame]) / 2.0);
originLeft.x -= frameSize.width;
originLeft.y += (aRect.size.height / 2.0) - (frameSize.height / 2.0);
// CPMaxYEdge
originBottom.x += aRect.size.width / 2.0 - CGRectGetWidth([self frame]) / 2.0;
originBottom.x += aRect.size.width / 2.0 - frameSize.width / 2.0;
originBottom.y += aRect.size.height;
// CPMinYEdge
originTop.x += aRect.size.width / 2.0 - CGRectGetWidth([self frame]) / 2.0;
originTop.y -= CGRectGetHeight([self frame]);
originTop.x += aRect.size.width / 2.0 - frameSize.width / 2.0;
originTop.y -= frameSize.height;
var requestedEdge = (anEdge !== nil) ? anEdge : CPMaxXEdge,
requestedOrigin;
@@ -301,20 +300,20 @@ var _CPAttachedWindow_attachedWindowShouldClose_ = 1 << 0,
[_windowView setArrowOffsetX:o.x];
o.x = 0;
}
if (o.x + CGRectGetWidth([self frame]) > nativeRect.size.width)
if (o.x + frameSize.width > nativeRect.size.width)
{
[_windowView setArrowOffsetX:(o.x + CGRectGetWidth([self frame]) - nativeRect.size.width)];
o.x = nativeRect.size.width - CGRectGetWidth([self frame]);
[_windowView setArrowOffsetX:(o.x + frameSize.width - nativeRect.size.width)];
o.x = nativeRect.size.width - frameSize.width;
}
if (o.y < 0)
{
[_windowView setArrowOffsetY:o.y];
o.y = 0;
}
if (o.y + CGRectGetHeight([self frame]) > nativeRect.size.height)
if (o.y + frameSize.height > nativeRect.size.height)
{
[_windowView setArrowOffsetY:(CGRectGetHeight([self frame]) + o.y - nativeRect.size.height)];
o.y = nativeRect.size.height - CGRectGetHeight([self frame]);
[_windowView setArrowOffsetY:(frameSize.height + o.y - nativeRect.size.height)];
o.y = nativeRect.size.height - frameSize.height;
}
switch (g)
@@ -324,7 +323,7 @@ var _CPAttachedWindow_attachedWindowShouldClose_ = 1 << 0,
return o;
break;
case CPMinXEdge:
if ((o.x + _frame.size.width) <= aRect.origin.x)
if ((o.x + frameSize.width) <= aRect.origin.x)
return o;
break;
case CPMaxYEdge:
@@ -332,7 +331,7 @@ var _CPAttachedWindow_attachedWindowShouldClose_ = 1 << 0,
return o;
break;
case CPMinYEdge:
if ((o.y + _frame.size.height) <= aRect.origin.y)
if ((o.y + frameSize.height) <= aRect.origin.y)
return o;
break;
}
@@ -403,6 +402,27 @@ var _CPAttachedWindow_attachedWindowShouldClose_ = 1 << 0,
[self makeKeyAndOrderFront:nil];
}
/*! @ignore */
- (void)setCSS3Property:(CPString)property value:(CPString)value
{
_DOMElement.style['webkit' + property] = value;
// Support other browsers here eventually
}
/*! @ignore */
- (BOOL)browserSupportsAnimation
{
return typeof(_DOMElement.style.webkitTransition) !== "undefined";
/*
No others browsers supported yet.
typeof(_DOMElement.style.MozTransition) !== "undefined" ||
typeof(_DOMElement.style.MsTransition) !== "undefined" ||
typeof(_DOMElement.style.OTransition) !== "undefined";
*/
}
#pragma mark -
#pragma mark Actions
@@ -443,63 +463,65 @@ var _CPAttachedWindow_attachedWindowShouldClose_ = 1 << 0,
{
[super orderFront:aSender];
var transformOrigin = "50% 100%";
if (_animationStyle === CPPopoverAnimationStyleLion)
if (_animates && _browserAnimates && _shouldPerformAnimation)
{
switch ([_windowView preferredEdge])
{
case CPMaxYEdge:
var posX = 50 + (([_windowView arrowOffsetX] * 100) / _frame.size.width);
transformOrigin = posX + "% 0%"; // 50 0
break;
case CPMinYEdge:
var posX = 50 + (([_windowView arrowOffsetX] * 100) / _frame.size.width);
transformOrigin = posX + "% 100%"; // 50 100
break;
case CPMinXEdge:
var posY = 50 + (([_windowView arrowOffsetY] * 100) / _frame.size.height);
transformOrigin = "100% " + posY + "%"; // 100 50
break;
case CPMaxXEdge:
var posY = 50 + (([_windowView arrowOffsetY] * 100) / _frame.size.height);
transformOrigin = "0% "+ posY + "%"; // 0 50
break;
}
}
// @TODO: implement for FF
if (_animates && _shouldPerformAnimation && typeof(_DOMElement.style.webkitTransform) != "undefined")
{
_DOMElement.style.opacity = 0;
if (_animationStyle === CPPopoverAnimationStyleLion)
{
_DOMElement.style.webkitTransform = "scale(0)";
_DOMElement.style.webkitTransformOrigin = transformOrigin;
}
var transformOrigin = "50% 100%",
frame = [self frame],
preferredEdge = [_windowView preferredEdge],
posX;
window.setTimeout(function()
{
_DOMElement.style.opacity = 1;
_DOMElement.style.height = _frame.size.height + @"px";
_DOMElement.style.width = _frame.size.width + @"px";
if (_animationStyle === CPPopoverAnimationStyleLion)
switch (preferredEdge)
{
_DOMElement.style.webkitTransform = "scale(1.1)";
_DOMElement.style.webkitTransitionDuration = "250ms";
_DOMElement.style.webkitTransitionTimingFunction = "ease-out";
case CPMaxYEdge:
case CPMinYEdge:
posX = 50 + (([_windowView arrowOffsetX] * 100) / frame.size.width);
transformOrigin = posX + "% " + (preferredEdge === CPMaxYEdge ? "0%" : "100%");
break;
var transitionEndFunction = function()
{
_DOMElement.style.webkitTransform = "scale(1)";
_DOMElement.removeEventListener("webkitTransitionEnd", transitionEndFunction, YES);
};
_DOMElement.addEventListener("webkitTransitionEnd", transitionEndFunction, YES);
case CPMinXEdge:
case CPMaxXEdge:
posY = 50 + (([_windowView arrowOffsetY] * 100) / frame.size.height);
transformOrigin = (preferredEdge === CPMaxXEdge ? "0% " : "100% ") + posY + "%"; // 100 50
break;
}
}, 0);
// This is the initial transform
[self setCSS3Property:@"Transform" value:@"scale(0)"];
[self setCSS3Property:@"TransformOrigin" value:transformOrigin];
[self setCSS3Property:@"TransitionDuration" value:"0"];
window.setTimeout(function()
{
if (_animationStyle === CPPopoverAnimationStyleLion)
{
// We are watching opacity, so this triggers the next transition
_DOMElement.style.opacity = 1;
_DOMElement.style.height = frame.size.height + @"px";
_DOMElement.style.width = frame.size.width + @"px";
// Set up the pop-out transition
[self setCSS3Property:@"Transform" value:@"scale(1.1)"];
[self setCSS3Property:@"TransitionDuration" value:@"200ms"];
[self setCSS3Property:@"TransitionTimingFunction" value:@"ease-in"];
var transitionEndFunction = function()
{
_DOMElement.removeEventListener("webkitTransitionEnd", transitionEndFunction, YES);
// Now set up the pop-in to normal size transition
[self setCSS3Property:@"Transform" value:@"scale(1)"];
[self setCSS3Property:@"TransitionDuration" value:@"50ms"];
[self setCSS3Property:@"TransitionTimingFunction" value:@"linear"];
};
_DOMElement.addEventListener("webkitTransitionEnd", transitionEndFunction, YES);
}
}, 0);
}
else
_DOMElement.style.opacity = 1;
}
[[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_attachedWindowDidMove:) name:CPWindowDidMoveNotification object:self];
@@ -516,11 +538,10 @@ var _CPAttachedWindow_attachedWindowShouldClose_ = 1 << 0,
// set a close flag to avoid infinite loop
_isClosed = YES;
if (_animates && typeof(_DOMElement.style.webkitTransform) != "undefined")
if (_animates && _browserAnimates)
{
[self setCSS3Property:@"Transition" value:@"opacity 250ms linear"];
_DOMElement.style.opacity = 0;
_DOMElement.style.webkitTransitionDuration = "250ms";
_DOMElement.style.webkitTransitionTimingFunction = "linear";
var transitionEndFunction = function()
{
+100 -50
View File
@@ -12,39 +12,72 @@
@implementation AppController : CPObject
{
CPPopUpButton buttonGravity;
CPPopUpButton buttonEdge;
CPPopUpButton buttonStyle;
CPPopUpButton buttonAnimation;
CPPopUpButton buttonBehaviour;
CPPopover popover;
CPTextField appearanceLabel;
}
- (void)applicationDidFinishLaunching:(CPNotification)aNotification
{
var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask],
contentView = [theWindow contentView];
contentView = [theWindow contentView],
contentViewSize = [contentView frameSize];
var button = [CPButton buttonWithTitle:@"click"];
[button setTarget:self];
[button setAction:@selector(open:)];
[button setFrameOrigin:CPPointMake(10, 60)];
[button setFrameOrigin:CGPointMake(1, 1)];
[contentView addSubview:button];
button = [CPButton buttonWithTitle:@"click"];
[button setTarget:self];
[button setAction:@selector(open:)];
[button setFrameOrigin:CPPointMake( [contentView frameSize].width - 50, 60)];
[button setFrameOrigin:CGPointMake(1, 100)];
[contentView addSubview:button];
button = [CPButton buttonWithTitle:@"click"];
[button setTarget:self];
[button setAction:@selector(open:)];
[button setFrameOrigin:CPPointMake( [contentView frameSize].width - 50, [contentView frameSize].height - 50)];
[button setAutoresizingMask:CPViewMinXMargin]
[button setFrameOrigin:CGPointMake(contentViewSize.width - CGRectGetWidth([button frame]) - 1, 1)];
[contentView addSubview:button];
button = [CPButton buttonWithTitle:@"click"];
[button setTarget:self];
[button setAction:@selector(open:)];
[button setFrameOrigin:CPPointMake( 10, [contentView frameSize].height - 50)];
[button setAutoresizingMask:CPViewMinXMargin]
[button setFrameOrigin:CGPointMake(contentViewSize.width - CGRectGetWidth([button frame]) - 1, 100)];
[contentView addSubview:button];
button = [CPButton buttonWithTitle:@"click"];
[button setTarget:self];
[button setAction:@selector(open:)];
[button setAutoresizingMask:CPViewMinXMargin | CPViewMinYMargin]
[button setFrameOrigin:CGPointMake(contentViewSize.width - CGRectGetWidth([button frame])- 1, contentViewSize.height - CGRectGetHeight([button frame]) - 1)];
[contentView addSubview:button];
button = [CPButton buttonWithTitle:@"click"];
[button setTarget:self];
[button setAction:@selector(open:)];
[button setAutoresizingMask:CPViewMinXMargin | CPViewMinYMargin]
[button setFrameOrigin:CGPointMake(contentViewSize.width - CGRectGetWidth([button frame])- 1, contentViewSize.height - 100)];
[contentView addSubview:button];
button = [CPButton buttonWithTitle:@"click"];
[button setTarget:self];
[button setAction:@selector(open:)];
[button setAutoresizingMask:CPViewMaxXMargin | CPViewMinYMargin]
[button setFrameOrigin:CGPointMake(1, contentViewSize.height - CGRectGetHeight([button frame]) - 1)];
[contentView addSubview:button];
button = [CPButton buttonWithTitle:@"click"];
[button setTarget:self];
[button setAction:@selector(open:)];
[button setAutoresizingMask:CPViewMaxXMargin | CPViewMinYMargin]
[button setFrameOrigin:CGPointMake(1, contentViewSize.height - 100)];
[contentView addSubview:button];
button = [CPButton buttonWithTitle:@"click"];
@@ -55,30 +88,30 @@
[contentView addSubview:button];
buttonGravity = [[CPPopUpButton alloc] initWithFrame:CPRectMake(10, 10, 130, 24)];
[buttonGravity addItemWithTitle:"Automatic"];
[buttonGravity addItemWithTitle:"Bottom"];
[buttonGravity addItemWithTitle:"Top"];
[buttonGravity addItemWithTitle:"Right"];
[buttonGravity addItemWithTitle:"Left"];
[contentView addSubview:buttonGravity];
buttonEdge = [[CPPopUpButton alloc] initWithFrame:CGRectMake(150, 10, 130, 24)];
[buttonEdge addItemWithTitle:"Automatic"];
[buttonEdge addItemWithTitle:"Bottom"];
[buttonEdge addItemWithTitle:"Top"];
[buttonEdge addItemWithTitle:"Right"];
[buttonEdge addItemWithTitle:"Left"];
[contentView addSubview:buttonEdge];
buttonStyle = [[CPPopUpButton alloc] initWithFrame:CPRectMake(150, 10, 130, 24)];
buttonStyle = [[CPPopUpButton alloc] initWithFrame:CGRectMake(290, 10, 130, 24)];
[buttonStyle addItemWithTitle:"Minimal"];
[buttonStyle addItemWithTitle:"HUD"];
[contentView addSubview:buttonStyle];
buttonAnimation = [[CPPopUpButton alloc] initWithFrame:CPRectMake(290, 10, 130, 24)];
buttonAnimation = [[CPPopUpButton alloc] initWithFrame:CGRectMake(430, 10, 130, 24)];
[buttonAnimation addItemWithTitle:"With animation"];
[buttonAnimation addItemWithTitle:"No animation"];
[contentView addSubview:buttonAnimation];
buttonAnimationStyle = [[CPPopUpButton alloc] initWithFrame:CPRectMake(430, 10, 130, 24)];
buttonAnimationStyle = [[CPPopUpButton alloc] initWithFrame:CGRectMake(570, 10, 130, 24)];
[buttonAnimationStyle addItemWithTitle:"Lion"];
[buttonAnimationStyle addItemWithTitle:"iOS"];
[contentView addSubview:buttonAnimationStyle];
buttonBehaviour = [[CPPopUpButton alloc] initWithFrame:CPRectMake(570, 10, 130, 24)];
buttonBehaviour = [[CPPopUpButton alloc] initWithFrame:CGRectMake(710, 10, 130, 24)];
[buttonBehaviour addItemWithTitle:"Transient"];
[buttonBehaviour addItemWithTitle:"Not managed"];
[contentView addSubview:buttonBehaviour];
@@ -88,64 +121,81 @@
- (IBAction)open:(id)sender
{
var g;
switch ([buttonGravity title])
var edge;
switch ([buttonEdge title])
{
case "Automatic":
g = nil;
edge = nil;
break;
case "Bottom":
g = CPMaxYEdge;
edge = CPMaxYEdge;
break;
case "Top":
g = CPMinYEdge;
edge = CPMinYEdge;
break;
case "Left":
g = CPMinXEdge;
edge = CPMinXEdge;
break;
case "Right":
g = CPMaxXEdge;
edge = CPMaxXEdge;
break;
}
var a;
var appearance;
switch ([buttonStyle title])
{
case "Minimal":
a = CPPopoverAppearanceMinimal;
appearance = CPPopoverAppearanceMinimal;
break;
case "HUD":
a = CPPopoverAppearanceHUD;
appearance = CPPopoverAppearanceHUD;
break;
}
var p = [[CPPopover alloc] init],
viewC = [[CPViewController alloc] init],
view = [[CPView alloc] initWithFrame:CPRectMake(0.0, 0.0, 320, 300)],
label = [CPTextField labelWithTitle:[buttonGravity title]];
var pop = [self popoverWithAppearance:appearance];
[label setFont:[CPFont boldSystemFontOfSize:30.0]];
[label setFrameOrigin:CPPointMake(0, 70)];
[label setValue:(a === CPPopoverAppearanceHUD) ? [CPColor colorWithHexString:@"333"] : [CPColor colorWithHexString:@"fff"] forThemeAttribute:@"text-shadow-color"];
[label setValue:CGSizeMake(0.0, 1.0) forThemeAttribute:@"text-shadow-offset"];
[label setTextColor:(a === CPPopoverAppearanceHUD) ? [CPColor whiteColor] : [CPColor colorWithHexString:@"444"]];
[label setFrameSize:CPSizeMake([view frame].size.width, 50)];
[label setAlignment:CPCenterTextAlignment];
[view addSubview:label];
[pop showRelativeToRect:nil ofView:sender preferredEdge:edge];
[viewC setView:view];
[p setContentViewController:viewC];
[p setAnimates:([buttonAnimation title] === @"With animation")];
[p setAnimationStyle:[buttonAnimationStyle title] === @"Lion" ? CPPopoverAnimationStyleLion : CPPopoverAnimationStyleIOS];
[p setBehaviour:([buttonBehaviour title] === @"Transient") ? CPPopoverBehaviorTransient : CPPopoverBehaviorApplicationDefined];
[p setAppearance:a];
[p setDelegate:self];
[p showRelativeToRect:nil ofView:sender preferredEdge:g];
CPLog.info("content size - w:" + [p contentSize].width + " h:" + [p contentSize].width);
CPLog.info("positioning rect - x: " + [p positioningRect].origin.x + " y: " + [p positioningRect].origin.x
+ " w:" + [p positioningRect].size.width + " h:" + [p positioningRect].size.width);
CPLog.info("content size - w:" + [pop contentSize].width + " h:" + [pop contentSize].width);
CPLog.info("positioning rect - x: " + [pop positioningRect].origin.x + " y: " + [pop positioningRect].origin.x
+ " w:" + [pop positioningRect].size.width + " h:" + [pop positioningRect].size.width);
}
- (CPPopover)popoverWithAppearance:(int)appearance
{
if (popover)
{
[appearanceLabel setStringValue:[buttonEdge title]];
return popover;
}
popover = [CPPopover new];
var controller = [[CPViewController alloc] init],
view = [[CPView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320, 300)];
appearanceLabel = [CPTextField labelWithTitle:[buttonEdge title]];
[appearanceLabel setFont:[CPFont boldSystemFontOfSize:30.0]];
[appearanceLabel setFrameOrigin:CGPointMake(0, 70)];
[appearanceLabel setValue:(appearance === CPPopoverAppearanceHUD) ? [CPColor colorWithHexString:@"333"] : [CPColor colorWithHexString:@"fff"] forThemeAttribute:@"text-shadow-color"];
[appearanceLabel setValue:CGSizeMake(0.0, 1.0) forThemeAttribute:@"text-shadow-offset"];
[appearanceLabel setTextColor:(appearance === CPPopoverAppearanceHUD) ? [CPColor whiteColor] : [CPColor colorWithHexString:@"444"]];
[appearanceLabel setFrameSize:CPSizeMake([view frame].size.width, 50)];
[appearanceLabel setAlignment:CPCenterTextAlignment];
[view addSubview:appearanceLabel];
[controller setView:view];
[popover setContentViewController:controller];
[popover setAnimates:([buttonAnimation title] === @"With animation")];
[popover setAnimationStyle:[buttonAnimationStyle title] === @"Lion" ? CPPopoverAnimationStyleLion : CPPopoverAnimationStyleIOS];
[popover setBehaviour:([buttonBehaviour title] === @"Transient") ? CPPopoverBehaviorTransient : CPPopoverBehaviorApplicationDefined];
[popover setAppearance:appearance];
[popover setDelegate:self];
return popover;
}
#pragma mark -
#pragma mark CPPopover Delegate