mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-10-09 18:30:59 +00:00
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:
1 parent
28ed2f9bd0
commit
1a8fc7f07f
3 files changed
+381
-222
No files matched your search
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user