blob: a7f798fa180ecc6ac80e23f47a9d6a351db714f3 [file] [log] [blame] [edit]
//
// WEPopoverContainerViewProperties.m
// WEPopover
//
// Created by Werner Altewischer on 02/09/10.
// Copyright 2010 Werner IT Consultancy. All rights reserved.
//
static const CGFloat NLPopoverAlpha = 0.96f;
static const CGFloat ArrowDropShadowOffset = 1.0f;
#import "WEPopoverContainerView.h"
@implementation WEPopoverContainerViewProperties
@synthesize bgImageName, upArrowImageName, downArrowImageName, leftArrowImageName, rightArrowImageName, topBgMargin, bottomBgMargin, leftBgMargin, rightBgMargin, topBgCapSize, leftBgCapSize;
@synthesize leftContentMargin, rightContentMargin, topContentMargin, bottomContentMargin, arrowMargin;
@end
@interface WEPopoverContainerView(Private)
- (void)determineGeometryForSize:(CGSize)theSize anchorRect:(CGRect)anchorRect displayArea:(CGRect)displayArea permittedArrowDirections:(UIPopoverArrowDirection)permittedArrowDirections;
- (CGRect)contentRect;
- (CGSize)contentSize;
- (void)setProperties:(WEPopoverContainerViewProperties *)props;
- (void)initFrame;
@end
@implementation WEPopoverContainerView
@synthesize arrowDirection, contentView;
- (id)initWithSize:(CGSize)theSize
anchorRect:(CGRect)anchorRect
displayArea:(CGRect)displayArea
permittedArrowDirections:(UIPopoverArrowDirection)permittedArrowDirections
properties:(WEPopoverContainerViewProperties *)theProperties {
if ((self = [super initWithFrame:CGRectZero])) {
[self setProperties:theProperties];
correctedSize = CGSizeMake(theSize.width + properties.leftBgMargin + properties.rightBgMargin + properties.leftContentMargin + properties.rightContentMargin,
theSize.height + properties.topBgMargin + properties.bottomBgMargin + properties.topContentMargin + properties.bottomContentMargin);
[self determineGeometryForSize:correctedSize anchorRect:anchorRect displayArea:displayArea permittedArrowDirections:permittedArrowDirections];
[self initFrame];
self.backgroundColor = [UIColor clearColor];
UIImage *theImage = [UIImage imageNamed:properties.bgImageName];
bgImage = [theImage stretchableImageWithLeftCapWidth:properties.leftBgCapSize topCapHeight:properties.topBgCapSize];
self.clipsToBounds = YES;
self.userInteractionEnabled = YES;
self.layer.shadowOffset = CGSizeMake(3, 3);
self.layer.shadowRadius = 6;
self.layer.shadowOpacity = 0.3f;
self.clipsToBounds = NO;
}
return self;
}
- (void)drawRect:(CGRect)rect {
[bgImage drawInRect:bgRect blendMode:kCGBlendModeNormal alpha:NLPopoverAlpha];
[arrowImage drawInRect:arrowRect blendMode:kCGBlendModeNormal alpha:NLPopoverAlpha];
}
- (void)updatePositionWithAnchorRect:(CGRect)anchorRect
displayArea:(CGRect)displayArea
permittedArrowDirections:(UIPopoverArrowDirection)permittedArrowDirections {
[self determineGeometryForSize:correctedSize anchorRect:anchorRect displayArea:displayArea permittedArrowDirections:permittedArrowDirections];
[self initFrame];
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
return CGRectContainsPoint(self.contentRect, point);
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
}
- (void)setContentView:(UIView *)v {
if (v != contentView) {
contentView = v;
contentView.frame = self.contentRect;
[self addSubview:contentView];
}
}
@end
@implementation WEPopoverContainerView(Private)
- (void)initFrame {
CGRect theFrame = CGRectOffset(CGRectUnion(bgRect, arrowRect), offset.x, offset.y);
//If arrow rect origin is < 0 the frame above is extended to include it so we should offset the other rects
arrowOffset = CGPointMake(MAX(0, -arrowRect.origin.x), MAX(0, -arrowRect.origin.y));
bgRect = CGRectOffset(bgRect, arrowOffset.x, arrowOffset.y);
arrowRect = CGRectOffset(arrowRect, arrowOffset.x, arrowOffset.y);
self.frame = theFrame;
}
- (CGSize)contentSize {
return self.contentRect.size;
}
- (CGRect)contentRect {
CGRect rect = CGRectMake(properties.leftBgMargin + properties.leftContentMargin + arrowOffset.x,
properties.topBgMargin + properties.topContentMargin + arrowOffset.y,
bgRect.size.width - properties.leftBgMargin - properties.rightBgMargin - properties.leftContentMargin - properties.rightContentMargin,
bgRect.size.height - properties.topBgMargin - properties.bottomBgMargin - properties.topContentMargin - properties.bottomContentMargin);
return rect;
}
- (void)setProperties:(WEPopoverContainerViewProperties *)props {
if (properties != props) {
properties = props;
}
}
// NOTE: this method is completely rewritten by Nest Labs to do layout that meets our UI needs.
// It's kinda shoehorned in so we didn't need to modify other WEPopover code.
// It's not a general solution, does not obey supportedArrowDirections, and has bugs.
// Sorry.
- (void)determineGeometryForSize:(CGSize)theSize anchorRect:(CGRect)anchorRect displayArea:(CGRect)displayArea permittedArrowDirections:(UIPopoverArrowDirection)supportedArrowDirections {
UIImage *downArrowImage = [UIImage imageNamed:properties.downArrowImageName];
UIImage *leftArrowImage = [UIImage imageNamed:properties.leftArrowImageName];
UIImage *rightArrowImage = [UIImage imageNamed:properties.rightArrowImageName];
UIImage *upArrowImage = [UIImage imageNamed:properties.upArrowImageName];
// try displaying above anchor
CGFloat topFreeSpace = (anchorRect.origin.y - (theSize.height + downArrowImage.size.height)) - displayArea.origin.y;
CGFloat leftFreeSpace = CGRectGetMidX(anchorRect) - CGRectGetMinX(displayArea);
CGFloat rightFreeSpace = CGRectGetMaxX(displayArea) - CGRectGetMidX(anchorRect);
if ((topFreeSpace >= 0) && (leftFreeSpace >= (rightArrowImage.size.width / 2) + 16) && (rightFreeSpace >= (leftArrowImage.size.width / 2) + 16)) {
bgRect = CGRectMake(0, 0, theSize.width, theSize.height);
// Substarct 1 from actual top to overlap arrow image and background image.
arrowRect = CGRectMake((theSize.width - downArrowImage.size.width) / 2, (theSize.height - ArrowDropShadowOffset), downArrowImage.size.width, downArrowImage.size.height);
offset = CGPointMake(CGRectGetMidX(anchorRect) - (bgRect.size.width / 2), -(arrowRect.origin.y + arrowRect.size.height - anchorRect.origin.y));
arrowDirection = UIPopoverArrowDirectionDown;
arrowImage = downArrowImage;
// ...check for going off right edge
CGFloat overage = (bgRect.size.width + offset.x) - (displayArea.origin.x + displayArea.size.width);
if (overage > 0) {
arrowRect = CGRectOffset(arrowRect, overage, 0);
offset.x -= overage;
}
// ...check for going off left edge
CGFloat underage = offset.x - displayArea.origin.x;
if (underage < 0) {
arrowRect = CGRectOffset(arrowRect, underage, 0);
offset.x -= underage;
}
} else if (((leftFreeSpace - (theSize.width + rightArrowImage.size.width)) > 0) ||
((rightFreeSpace - (theSize.width + leftArrowImage.size.width)) > 0)) {
// try left or right side
if (leftFreeSpace > rightFreeSpace) {
// ...to left
bgRect = CGRectMake(0, 0, theSize.width, theSize.height);
// Substarct 1 from actual left to overlap arrow image and background image.
arrowRect = CGRectMake(theSize.width - ArrowDropShadowOffset, (theSize.height - rightArrowImage.size.height) / 2, rightArrowImage.size.width, rightArrowImage.size.height);
offset = CGPointMake((anchorRect.origin.x - theSize.width - rightArrowImage.size.width), -((theSize.height - anchorRect.size.height) / 2) + anchorRect.origin.y);
arrowDirection = UIPopoverArrowDirectionRight;
arrowImage = rightArrowImage;
} else {
// ...to right
bgRect = CGRectMake(0, 0, theSize.width, theSize.height);
// Add 1 to actual left to overlap arrow image and background image.
arrowRect = CGRectMake(-leftArrowImage.size.width + ArrowDropShadowOffset, (theSize.height - leftArrowImage.size.height) / 2, leftArrowImage.size.width, leftArrowImage.size.height);
offset = CGPointMake((anchorRect.origin.x + anchorRect.size.width + leftArrowImage.size.width), -((theSize.height - anchorRect.size.height) / 2) + anchorRect.origin.y);
arrowDirection = UIPopoverArrowDirectionLeft;
arrowImage = leftArrowImage;
}
// ...check for going off bottom
CGFloat overage = (CGRectGetMaxY(bgRect) + offset.y) - CGRectGetMaxY(displayArea);
if (overage > 0) {
overage = MIN(overage, CGRectGetMaxY(bgRect) - CGRectGetMaxY(arrowRect) - 12);
arrowRect = CGRectOffset(arrowRect, 0, overage);
offset.y -= overage;
}
// ...check for going off top
CGFloat underage = (CGRectGetMinY(bgRect) + offset.y) - CGRectGetMinY(displayArea);
if (underage < 0) {
arrowRect = CGRectOffset(arrowRect, 0, underage);
offset.y -= underage;
}
} else {
bgRect = CGRectMake(0, 0, theSize.width, theSize.height);
// Add 1 to actual top to overlap arrow image and background image.
arrowRect = CGRectMake((theSize.width - upArrowImage.size.width) / 2, - (upArrowImage.size.height - 1), upArrowImage.size.width, upArrowImage.size.height);
offset = CGPointMake(CGRectGetMidX(anchorRect) - (bgRect.size.width / 2), CGRectGetMaxY(anchorRect) + upArrowImage.size.height);
arrowDirection = UIPopoverArrowDirectionUp;
arrowImage = upArrowImage;
// ...check for going off right edge
CGFloat overage = (bgRect.size.width + offset.x) - (displayArea.origin.x + displayArea.size.width);
if (overage > 0) {
arrowRect = CGRectOffset(arrowRect, overage, 0);
offset.x -= overage;
}
// ...check for going off left edge
CGFloat underage = offset.x - displayArea.origin.x;
if (underage < 0) {
arrowRect = CGRectOffset(arrowRect, underage, 0);
offset.x -= underage;
}
}
}
@end