diff options
Diffstat (limited to 'frontends/cocoa/ArrowWindow.m')
-rw-r--r-- | frontends/cocoa/ArrowWindow.m | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/frontends/cocoa/ArrowWindow.m b/frontends/cocoa/ArrowWindow.m new file mode 100644 index 000000000..720e55e29 --- /dev/null +++ b/frontends/cocoa/ArrowWindow.m @@ -0,0 +1,240 @@ +/* Copyright 2011 Sven Weidauer + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#import "ArrowWindow.h" +#import "ArrowBox.h" + +@implementation ArrowWindow + +@synthesize acceptsKey; + +- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSWindowStyleMask)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag +{ + if ((self = [super initWithContentRect:contentRect styleMask:NSWindowStyleMaskBorderless backing:bufferingType defer:flag]) == nil) { + return nil; + } + + [self setBackgroundColor:[NSColor clearColor]]; + [self setOpaque:NO]; + [self setHasShadow:YES]; + + return self; +} + +- (void)setContentView:(NSView *)aView +{ + if (aView == content) + return; + + [content removeFromSuperview]; + content = aView; + + if (content == nil) + return; + + if (box == nil) { + box = [[ArrowBox alloc] initWithFrame:NSZeroRect]; + [box setArrowEdge:ArrowTopEdge]; + [super setContentView:box]; + } + + [box addSubview:content]; + + NSRect frame = [self contentRectForFrameRect:[self frame]]; + frame.origin = [self convertRectFromScreen:(CGRect){.origin = frame.origin }].origin; + frame = [box convertRect:frame fromView:nil]; + + [content setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [content setFrame:frame]; +} + +- (void)setContentSize:(NSSize)aSize +{ + NSRect frame = [content frame]; + frame.size = aSize; + + frame = [box convertRect:frame toView:nil]; + frame.origin = [self convertRectToScreen:(CGRect){.origin = frame.origin }].origin; + frame = [self frameRectForContentRect:frame]; + + [self setFrame:frame display:YES]; +} + +static const CGFloat padding = 0; + +- (NSRect)contentRectForFrameRect:(NSRect)frameRect +{ + const CGFloat arrowSize = [box arrowSize]; + const CGFloat offset = 2 * (padding + arrowSize); + + return NSInsetRect(frameRect, offset, offset); +} + +- (NSRect)frameRectForContentRect:(NSRect)contentRect +{ + const CGFloat arrowSize = [box arrowSize]; + const CGFloat offset = -2 * (padding + arrowSize); + + return NSInsetRect(contentRect, offset, offset); +} + ++ (NSRect)frameRectForContentRect:(NSRect)cRect styleMask:(NSWindowStyleMask)aStyle +{ + const CGFloat DefaultArrowSize = 15; + const CGFloat offset = -2 * (padding + DefaultArrowSize); + + return NSInsetRect(cRect, offset, offset); +} + +- (BOOL)canBecomeKeyWindow +{ + return acceptsKey; +} + +- (void)moveToPoint:(NSPoint)screenPoint +{ + switch ([box arrowEdge]) { + case ArrowNone: + screenPoint.x -= [box arrowSize]; + screenPoint.y += [box arrowSize]; + break; + + case ArrowTopEdge: + screenPoint.x -= [box arrowPosition]; + break; + + case ArrowBottomEdge: + screenPoint.x -= [box arrowPosition]; + screenPoint.y += NSHeight([self frame]); + break; + + case ArrowLeftEdge: + screenPoint.y += NSHeight([self frame]) - [box arrowPosition] - [box arrowSize]; + break; + + case ArrowRightEdge: + screenPoint.x -= NSWidth([self frame]); + screenPoint.y += NSHeight([self frame]) - [box arrowPosition] - [box arrowSize]; + break; + } + + [self setFrameTopLeftPoint:screenPoint]; +} + +static NSRect ScreenRectForView(NSView *view) +{ + NSRect viewRect = [view bounds]; // in View coordinate system + viewRect = [view convertRect:viewRect toView:nil]; // translate to window coordinates + viewRect.origin = [[view window] convertRectToScreen:(CGRect){.origin = viewRect.origin }].origin; // translate to screen coordinates + return viewRect; +} + +- (void)attachToView:(NSView *)view +{ + if (nil != attachedWindow) + [self detach]; + + NSRect viewRect = ScreenRectForView(view); + NSPoint arrowPoint; + + switch ([box arrowEdge]) { + case ArrowLeftEdge: + arrowPoint = NSMakePoint(NSMaxX(viewRect), + NSMidY(viewRect)); + break; + + case ArrowBottomEdge: + arrowPoint = NSMakePoint(NSMidX(viewRect), + NSMaxY(viewRect)); + break; + + case ArrowRightEdge: + arrowPoint = NSMakePoint(NSMinX(viewRect), + NSMidY(viewRect)); + break; + + case ArrowNone: + case ArrowTopEdge: + default: + arrowPoint = NSMakePoint(NSMidX(viewRect), + NSMinY(viewRect)); + break; + } + attachedWindow = [view window]; + [self moveToPoint:arrowPoint]; + [attachedWindow addChildWindow:self ordered:NSWindowAbove]; +} + +- (void)detach +{ + [attachedWindow removeChildWindow:self]; + [self close]; + attachedWindow = nil; +} + +//MARK: - +//MARK: Properties + +- (void)setArrowPosition:(CGFloat)newPosition +{ + [box setArrowPosition:newPosition]; +} + +- (CGFloat)arrowPosition +{ + return [box arrowPosition]; +} + +- (void)setArrowSize:(CGFloat)newSize +{ + NSRect contentRect = [self contentRectForFrameRect:[self frame]]; + [box setArrowSize:newSize]; + [self setFrame:[self frameRectForContentRect:contentRect] display:[self isVisible]]; +} + +- (CGFloat)arrowSize +{ + return [box arrowSize]; +} + +- (void)setArrowEdge:(ArrowEdge)newEdge +{ + [box setArrowEdge:newEdge]; +} + +- (ArrowEdge)arrowEdge +{ + return [box arrowEdge]; +} + +- (void)setCornerRadius:(CGFloat)newRadius +{ + [box setCornerRadius:newRadius]; +} + +- (CGFloat)cornerRadius +{ + return [box cornerRadius]; +} + +@end |