Skip to content
This repository was archived by the owner on Jun 8, 2023. It is now read-only.

Use next edge when preferred edge rect would be out of bounds #132

Merged
merged 23 commits into from
Apr 12, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
50a52ae
Separate arrow coordinate calculations from CGPath stuff
joshvera Apr 4, 2014
905a29e
Remove arrow shrinking
joshvera Apr 4, 2014
17f8464
Change demo to show popover relative to button bounds
joshvera Apr 4, 2014
c6ba986
Add popover anchor point and didOffsetFrame properties to RBLBackgrou…
joshvera Apr 4, 2014
d5cbe5b
Flip the edge if the proposed rect for the preferred edge is offscreen
joshvera Apr 4, 2014
2795024
Merge remote-tracking branch 'origin/master' into popover-edge-fixes
joshvera Apr 6, 2014
8846e44
Just set ivar
joshvera Apr 6, 2014
26ec75b
style
joshvera Apr 6, 2014
7c73e63
Replace M_PI / 2 with M_PI_2
joshvera Apr 6, 2014
92b4ad2
Replace NSMax uses with maxX, maxY
joshvera Apr 6, 2014
3f60097
Clean up popover path calculations
joshvera Apr 6, 2014
f28d565
Use next edge if screen rect doesn't contain the preferred edge
joshvera Apr 7, 2014
1fbeeaf
Dont use midOriginY
joshvera Apr 7, 2014
7a7b98e
Calculate based on window visible frame
joshvera Apr 7, 2014
4366d3e
Dont forget to set else case
joshvera Apr 7, 2014
4a2b244
Position positioningRect using -alignmentRectForFrame:
joshvera Apr 8, 2014
5a4c6d1
Go back to using the median x and y to calculate mid origin x and y
joshvera Apr 8, 2014
4d96766
Remove -didOffsetFrame
joshvera Apr 8, 2014
06aba4e
Remove -popoverAnchorPoint
joshvera Apr 8, 2014
725f53d
No need to set the popoverEdge again
joshvera Apr 8, 2014
deb6fcc
Clean up arrow variables
joshvera Apr 8, 2014
1bdf150
No need to draw beginning point
joshvera Apr 8, 2014
b44ae6a
s/Midpoint/Centerpoint
joshvera Apr 8, 2014
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Demos/RBLPopoverDemo/RBLPopoverDemo/RPDAppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ - (IBAction)showRBLPopover:(id)sender {
} 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];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are equivalent, FWIW.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

}
}

Expand Down
122 changes: 76 additions & 46 deletions Rebel/RBLPopover.m
Original file line number Diff line number Diff line change
Expand Up @@ -177,11 +177,11 @@ - (void)showRelativeToRect:(CGRect)positioningRect ofView:(NSView *)positioningV
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;

self.originalViewSize = self.contentViewController.view.frame.size;
CGSize contentViewSize = (CGSizeEqualToSize(self.contentSize, CGSizeZero) ? self.contentViewController.view.frame.size : self.contentSize);

Expand Down Expand Up @@ -269,27 +269,43 @@ - (void)showRelativeToRect:(CGRect)positioningRect ofView:(NSView *)positioningV

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;
}

popoverEdge = nextEdgeForEdge(popoverEdge);
attemptCount ++;
}

return popoverRectForEdge(popoverEdge);
};

CGRect popoverScreenRect = popoverRect();

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;
}

Expand Down Expand Up @@ -495,9 +511,9 @@ - (CGPathRef)newPopoverPathForEdge:(CGRectEdge)popoverEdge inFrame:(CGRect)frame

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;
CGFloat minArrowY = 0.0;
Expand All @@ -512,65 +528,79 @@ - (CGPathRef)newPopoverPathForEdge:(CGRectEdge)popoverEdge inFrame:(CGRect)frame
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);
}

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);
CGPathMoveToPoint(path, NULL, minX, minCenterpointY);

CGFloat radius = RBLPopoverBackgroundViewBorderRadius;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you're putting the radius into a variable here, can you use it in the (min|max)Midpoint(X|Y) calculations above? That way we're only using the constant in one place.


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 + 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;
}

Expand Down