Merge pull request #132 from github/popover-edge-fixes

Use next edge when preferred edge rect would be out of bounds
This commit is contained in:
Matt Diephouse 2014-04-12 07:53:39 -07:00
Родитель c7abdf5a4a b44ae6a0a2
Коммит 4a5ffefa13
2 изменённых файлов: 77 добавлений и 47 удалений

Просмотреть файл

@ -70,7 +70,7 @@
} else {
NSView *button = sender;
self.RBLPopover.behavior = self.behavior;
[self.RBLPopover showRelativeToRect:CGRectZero ofView:button preferredEdge:(CGRectEdge)self.preferredEdge];
[self.RBLPopover showRelativeToRect:button.bounds ofView:button preferredEdge:(CGRectEdge)self.preferredEdge];
}
}

Просмотреть файл

@ -177,7 +177,7 @@ static CGFloat RBLRectsGetMedianY(CGRect r1, CGRect r2) {
positioningRect = [positioningView bounds];
}
NSRect windowRelativeRect = [positioningView convertRect:positioningRect toView:nil];
NSRect windowRelativeRect = [positioningView convertRect:[positioningView alignmentRectForFrame:positioningRect] toView:nil];
CGRect screenRect = [positioningView.window convertRectToScreen:windowRelativeRect];
self.backgroundView.popoverOrigin = screenRect;
@ -270,10 +270,23 @@ static CGFloat RBLRectsGetMedianY(CGRect r1, CGRect r2) {
return proposedRect;
};
BOOL (^screenRectContainsRectEdge)(CGRectEdge) = ^ BOOL (CGRectEdge edge) {
CGRect proposedRect = popoverRectForEdge(edge);
NSRect screenRect = positioningView.window.screen.visibleFrame;
BOOL minYInBounds = (edge == CGRectMinYEdge && NSMinY(proposedRect) >= NSMinY(screenRect));
BOOL maxYInBounds = (edge == CGRectMaxYEdge && NSMaxY(proposedRect) <= NSMaxY(screenRect));
BOOL minXInBounds = (edge == CGRectMinXEdge && NSMinX(proposedRect) >= NSMinX(screenRect));
BOOL maxXInBounds = (edge == CGRectMaxXEdge && NSMaxX(proposedRect) <= NSMaxX(screenRect));
return minYInBounds || maxYInBounds || minXInBounds || maxXInBounds;
};
NSUInteger attemptCount = 0;
while (!checkPopoverSizeForScreenWithPopoverEdge(popoverEdge)) {
if (attemptCount >= 4) {
popoverEdge = preferredEdge;
popoverEdge = (screenRectContainsRectEdge(preferredEdge) ? preferredEdge : nextEdgeForEdge(preferredEdge));
return fitRectToScreen(popoverRectForEdge(popoverEdge));
break;
}
@ -289,7 +302,10 @@ static CGFloat RBLRectsGetMedianY(CGRect r1, CGRect r2) {
if (self.shown) {
if (self.backgroundView.popoverEdge == popoverEdge) {
CGSize size = [self.backgroundView sizeForBackgroundViewWithContentSize:contentViewSize popoverEdge:popoverEdge];
self.backgroundView.frame = (NSRect){ .size = size };
[self.popoverWindow setFrame:popoverScreenRect display:YES];
return;
}
@ -495,8 +511,8 @@ static CGFloat const RBLPopoverBackgroundViewArrowWidth = 35.0;
CGRect windowRect = [self.window convertRectFromScreen:self.popoverOrigin];
CGRect originRect = [self convertRect:windowRect fromView:nil];
CGFloat midOriginY = floor(RBLRectsGetMedianY(originRect, contentRect));
CGFloat midOriginX = floor(RBLRectsGetMedianX(originRect, contentRect));
CGFloat midOriginY = floor(RBLRectsGetMedianY(originRect, contentRect));
CGFloat maxArrowX = 0.0;
CGFloat minArrowX = 0.0;
@ -512,64 +528,78 @@ static CGFloat const RBLPopoverBackgroundViewArrowWidth = 35.0;
maxArrowX = floor(midOriginX + (self.arrowSize.width / 2.0));
CGFloat maxPossible = (NSMaxX(contentRect) - RBLPopoverBackgroundViewBorderRadius);
if (maxArrowX > maxPossible) {
CGFloat delta = maxArrowX - maxPossible;
maxArrowX = maxPossible;
minArrowX = maxArrowX - (self.arrowSize.width - delta);
minArrowX = maxArrowX - self.arrowSize.width;
} else {
minArrowX = floor(midOriginX - (self.arrowSize.width / 2.0));
if (minArrowX < RBLPopoverBackgroundViewBorderRadius) {
CGFloat delta = RBLPopoverBackgroundViewBorderRadius - minArrowX;
minArrowX = RBLPopoverBackgroundViewBorderRadius;
maxArrowX = minArrowX + (self.arrowSize.width - (delta * 2));
maxArrowX = minArrowX + self.arrowSize.width;
}
}
} else {
minArrowY = floor(midOriginY - (self.arrowSize.width / 2.0));
if (minArrowY < RBLPopoverBackgroundViewBorderRadius) {
CGFloat delta = RBLPopoverBackgroundViewBorderRadius - minArrowY;
minArrowY = RBLPopoverBackgroundViewBorderRadius;
maxArrowY = minArrowY + (self.arrowSize.width - (delta * 2));
maxArrowY = minArrowY + self.arrowSize.width;
} else {
maxArrowY = floor(midOriginY + (self.arrowSize.width / 2.0));
CGFloat maxPossible = (NSMaxY(contentRect) - RBLPopoverBackgroundViewBorderRadius);
if (maxArrowY > maxPossible) {
CGFloat delta = maxArrowY - maxPossible;
maxArrowY = maxPossible;
minArrowY = maxArrowY - (self.arrowSize.width - delta);
minArrowY = maxArrowY - self.arrowSize.width;
}
}
}
// These represent the centerpoints of the popover's corner arcs.
CGFloat minCenterpointX = floor(minX + RBLPopoverBackgroundViewBorderRadius);
CGFloat maxCenterpointX = floor(maxX - RBLPopoverBackgroundViewBorderRadius);
CGFloat minCenterpointY = floor(minY + RBLPopoverBackgroundViewBorderRadius);
CGFloat maxCenterpointY = floor(maxY - RBLPopoverBackgroundViewBorderRadius);
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, minX, floor(minY + RBLPopoverBackgroundViewBorderRadius));
if (arrowEdge == CGRectMinXEdge) {
CGPathAddLineToPoint(path, NULL, minX, minArrowY);
CGPathAddLineToPoint(path, NULL, floor(minX - self.arrowSize.height), midOriginY);
CGPathAddLineToPoint(path, NULL, minX, maxArrowY);
CGPathMoveToPoint(path, NULL, minX, minCenterpointY);
CGFloat radius = RBLPopoverBackgroundViewBorderRadius;
CGPathAddArc(path, NULL, minCenterpointX, maxCenterpointY, radius, M_PI, M_PI_2, true);
CGPathAddArc(path, NULL, maxCenterpointX, maxCenterpointY, radius, M_PI_2, 0, true);
CGPathAddArc(path, NULL, maxCenterpointX, minCenterpointY, radius, 0, -M_PI_2, true);
CGPathAddArc(path, NULL, minCenterpointX, minCenterpointY, radius, -M_PI_2, M_PI, true);
CGPoint minBasePoint, tipPoint, maxBasePoint;
switch (arrowEdge) {
case CGRectMinXEdge:
minBasePoint = CGPointMake(minX, minArrowY);
tipPoint = CGPointMake(floor(minX - self.arrowSize.height), floor((minArrowY + maxArrowY) / 2));
maxBasePoint = CGPointMake(minX, maxArrowY);
break;
case CGRectMaxYEdge:
minBasePoint = CGPointMake(minArrowX, maxY);
tipPoint = CGPointMake(floor((minArrowX + maxArrowX) / 2), floor(maxY + self.arrowSize.height));
maxBasePoint = CGPointMake(maxArrowX, maxY);
break;
case CGRectMaxXEdge:
minBasePoint = CGPointMake(maxX, minArrowY);
tipPoint = CGPointMake(floor(maxX + self.arrowSize.height), floor((minArrowY + maxArrowY) / 2));
maxBasePoint = CGPointMake(maxX, maxArrowY);
break;
case CGRectMinYEdge:
minBasePoint = CGPointMake(minArrowX, minY);
tipPoint = CGPointMake(floor((minArrowX + maxArrowX) / 2), floor(minY - self.arrowSize.height));
maxBasePoint = CGPointMake(maxArrowX, minY);
break;
default:
break;
}
CGPathAddArc(path, NULL, floor(minX + RBLPopoverBackgroundViewBorderRadius), floor(minY + contentRect.size.height - RBLPopoverBackgroundViewBorderRadius), RBLPopoverBackgroundViewBorderRadius, M_PI, M_PI / 2, 1);
if (arrowEdge == CGRectMaxYEdge) {
CGPathAddLineToPoint(path, NULL, minArrowX, maxY);
CGPathAddLineToPoint(path, NULL, midOriginX, floor(maxY + self.arrowSize.height));
CGPathAddLineToPoint(path, NULL, maxArrowX, maxY);
}
CGPathAddArc(path, NULL, floor(minX + contentRect.size.width - RBLPopoverBackgroundViewBorderRadius), floor(minY + contentRect.size.height - RBLPopoverBackgroundViewBorderRadius), RBLPopoverBackgroundViewBorderRadius, M_PI / 2, 0.0, 1);
if (arrowEdge == CGRectMaxXEdge) {
CGPathAddLineToPoint(path, NULL, maxX, maxArrowY);
CGPathAddLineToPoint(path, NULL, floor(maxX + self.arrowSize.height), midOriginY);
CGPathAddLineToPoint(path, NULL, maxX, minArrowY);
}
CGPathAddArc(path, NULL, floor(contentRect.origin.x + contentRect.size.width - RBLPopoverBackgroundViewBorderRadius), floor(minY + RBLPopoverBackgroundViewBorderRadius), RBLPopoverBackgroundViewBorderRadius, 0.0, -M_PI / 2, 1);
if (arrowEdge == CGRectMinYEdge) {
CGPathAddLineToPoint(path, NULL, maxArrowX, minY);
CGPathAddLineToPoint(path, NULL, midOriginX, floor(minY - self.arrowSize.height));
CGPathAddLineToPoint(path, NULL, minArrowX, minY);
}
CGPathAddArc(path, NULL, floor(minX + RBLPopoverBackgroundViewBorderRadius), floor(minY + RBLPopoverBackgroundViewBorderRadius), RBLPopoverBackgroundViewBorderRadius, -M_PI / 2, M_PI, 1);
CGPathMoveToPoint(path, NULL, minBasePoint.x, minBasePoint.y);
CGPathAddLineToPoint(path, NULL, tipPoint.x, tipPoint.y);
CGPathAddLineToPoint(path, NULL, maxBasePoint.x, maxBasePoint.y);
return path;
}