2016-02-09 00:10:51 +03:00
|
|
|
/* 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.
|
|
|
|
*/
|
2011-02-17 15:12:51 +03:00
|
|
|
|
|
|
|
#import "ArrowWindow.h"
|
|
|
|
#import "ArrowBox.h"
|
|
|
|
|
|
|
|
@implementation ArrowWindow
|
|
|
|
|
|
|
|
@synthesize acceptsKey;
|
|
|
|
|
|
|
|
- (id) initWithContentRect: (NSRect)contentRect styleMask: (NSUInteger)aStyle backing: (NSBackingStoreType)bufferingType defer: (BOOL)flag
|
|
|
|
{
|
2016-02-09 00:10:51 +03:00
|
|
|
if ((self = [super initWithContentRect: contentRect styleMask: NSBorderlessWindowMask 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 release];
|
|
|
|
}
|
|
|
|
|
|
|
|
[box addSubview: content];
|
|
|
|
|
|
|
|
NSRect frame = [self contentRectForFrameRect: [self frame]];
|
|
|
|
frame.origin = [self convertScreenToBase: frame.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 convertBaseToScreen: frame.origin];
|
|
|
|
frame = [self frameRectForContentRect: frame];
|
|
|
|
|
|
|
|
[self setFrame: frame display: YES];
|
2011-02-17 15:12:51 +03:00
|
|
|
}
|
|
|
|
|
2011-02-17 20:30:36 +03:00
|
|
|
static const CGFloat padding = 0;
|
2011-02-17 15:12:51 +03:00
|
|
|
|
2016-02-09 00:10:51 +03:00
|
|
|
- (NSRect) contentRectForFrameRect: (NSRect)frameRect
|
2011-02-17 15:12:51 +03:00
|
|
|
{
|
2016-02-09 00:10:51 +03:00
|
|
|
const CGFloat arrowSize = [box arrowSize];
|
|
|
|
const CGFloat offset = 2 * (padding + arrowSize );
|
2011-02-17 15:12:51 +03:00
|
|
|
|
2016-02-09 00:10:51 +03:00
|
|
|
return NSInsetRect( frameRect, offset, offset );
|
2011-02-17 15:12:51 +03:00
|
|
|
}
|
|
|
|
|
2016-02-09 00:10:51 +03:00
|
|
|
- (NSRect) frameRectForContentRect: (NSRect)contentRect
|
2011-02-17 15:12:51 +03:00
|
|
|
{
|
2016-02-09 00:10:51 +03:00
|
|
|
const CGFloat arrowSize = [box arrowSize];
|
|
|
|
const CGFloat offset = -2 * (padding + arrowSize );
|
|
|
|
|
|
|
|
return NSInsetRect( contentRect, offset, offset );
|
2011-02-17 15:12:51 +03:00
|
|
|
}
|
|
|
|
|
2016-02-09 00:10:51 +03:00
|
|
|
+ (NSRect) frameRectForContentRect: (NSRect)cRect styleMask: (NSUInteger)aStyle
|
2011-02-17 15:12:51 +03:00
|
|
|
{
|
2016-02-09 00:10:51 +03:00
|
|
|
const CGFloat DefaultArrowSize = 15;
|
|
|
|
const CGFloat offset = -2 * (padding + DefaultArrowSize);
|
|
|
|
|
|
|
|
return NSInsetRect( cRect, offset, offset );
|
2011-02-17 15:12:51 +03:00
|
|
|
}
|
|
|
|
|
2016-02-09 00:10:51 +03:00
|
|
|
- (BOOL) canBecomeKeyWindow
|
2011-02-17 15:12:51 +03:00
|
|
|
{
|
2016-02-09 00:10:51 +03:00
|
|
|
return acceptsKey;
|
2011-02-17 15:12:51 +03:00
|
|
|
}
|
|
|
|
|
2016-02-09 00:10:51 +03:00
|
|
|
- (void) moveToPoint: (NSPoint) screenPoint
|
2011-02-17 15:12:51 +03:00
|
|
|
{
|
2016-02-09 00:10:51 +03:00
|
|
|
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];
|
2011-02-17 15:12:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static NSRect ScreenRectForView( NSView *view )
|
|
|
|
{
|
2016-02-09 00:10:51 +03:00
|
|
|
NSRect viewRect = [view bounds]; // in View coordinate system
|
|
|
|
viewRect = [view convertRect: viewRect toView: nil]; // translate to window coordinates
|
|
|
|
viewRect.origin = [[view window] convertBaseToScreen: viewRect.origin]; // translate to screen coordinates
|
|
|
|
return viewRect;
|
2011-02-17 15:12:51 +03:00
|
|
|
}
|
|
|
|
|
2016-02-09 00:10:51 +03:00
|
|
|
- (void) attachToView: (NSView *) view
|
2011-02-17 15:12:51 +03:00
|
|
|
{
|
2016-02-09 00:10:51 +03:00
|
|
|
if (nil != attachedWindow) [self detach];
|
|
|
|
|
|
|
|
NSRect viewRect = ScreenRectForView( view );
|
|
|
|
NSPoint arrowPoint;
|
|
|
|
|
|
|
|
switch ([box arrowEdge]) {
|
|
|
|
case ArrowLeftEdge:
|
|
|
|
arrowPoint = NSMakePoint( NSMaxX( viewRect ),
|
|
|
|
NSMidY( viewRect ) );
|
|
|
|
break;
|
2011-02-17 15:12:51 +03:00
|
|
|
|
2016-02-09 00:10:51 +03:00
|
|
|
case ArrowBottomEdge:
|
|
|
|
arrowPoint = NSMakePoint( NSMidX( viewRect ),
|
|
|
|
NSMaxY( viewRect ) );
|
|
|
|
break;
|
2011-02-17 15:12:51 +03:00
|
|
|
|
2016-02-09 00:10:51 +03:00
|
|
|
case ArrowRightEdge:
|
|
|
|
arrowPoint = NSMakePoint( NSMinX( viewRect ),
|
|
|
|
NSMidY( viewRect ) );
|
|
|
|
break;
|
2016-05-02 15:12:43 +03:00
|
|
|
|
|
|
|
case ArrowNone:
|
|
|
|
case ArrowTopEdge:
|
|
|
|
default:
|
|
|
|
arrowPoint = NSMakePoint( NSMidX( viewRect ),
|
|
|
|
NSMinY( viewRect ) );
|
|
|
|
break;
|
|
|
|
|
2016-02-09 00:10:51 +03:00
|
|
|
}
|
|
|
|
attachedWindow = [view window];
|
|
|
|
[self moveToPoint: arrowPoint];
|
|
|
|
[attachedWindow addChildWindow: self ordered: NSWindowAbove];
|
2011-02-17 15:12:51 +03:00
|
|
|
}
|
|
|
|
|
2016-02-09 00:10:51 +03:00
|
|
|
- (void) detach
|
2011-02-17 15:12:51 +03:00
|
|
|
{
|
2016-02-09 00:10:51 +03:00
|
|
|
[attachedWindow removeChildWindow: self];
|
|
|
|
[self close];
|
|
|
|
attachedWindow = nil;
|
2011-02-17 15:12:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
//MARK: -
|
|
|
|
//MARK: Properties
|
|
|
|
|
2016-02-09 00:10:51 +03:00
|
|
|
- (void) setArrowPosition: (CGFloat) newPosition
|
2011-02-17 15:12:51 +03:00
|
|
|
{
|
2016-02-09 00:10:51 +03:00
|
|
|
[box setArrowPosition: newPosition];
|
2011-02-17 15:12:51 +03:00
|
|
|
}
|
|
|
|
|
2016-02-09 00:10:51 +03:00
|
|
|
- (CGFloat) arrowPosition
|
2011-02-17 15:12:51 +03:00
|
|
|
{
|
2016-02-09 00:10:51 +03:00
|
|
|
return [box arrowPosition];
|
2011-02-17 15:12:51 +03:00
|
|
|
}
|
|
|
|
|
2016-02-09 00:10:51 +03:00
|
|
|
- (void) setArrowSize: (CGFloat)newSize
|
2011-02-17 15:12:51 +03:00
|
|
|
{
|
2016-02-09 00:10:51 +03:00
|
|
|
NSRect contentRect = [self contentRectForFrameRect: [self frame]];
|
|
|
|
[box setArrowSize: newSize];
|
|
|
|
[self setFrame: [self frameRectForContentRect: contentRect] display: [self isVisible]];
|
2011-02-17 15:12:51 +03:00
|
|
|
}
|
|
|
|
|
2016-02-09 00:10:51 +03:00
|
|
|
- (CGFloat) arrowSize
|
2011-02-17 15:12:51 +03:00
|
|
|
{
|
2016-02-09 00:10:51 +03:00
|
|
|
return [box arrowSize];
|
2011-02-17 15:12:51 +03:00
|
|
|
}
|
|
|
|
|
2016-02-09 00:10:51 +03:00
|
|
|
- (void) setArrowEdge: (ArrowEdge) newEdge
|
2011-02-17 15:12:51 +03:00
|
|
|
{
|
2016-02-09 00:10:51 +03:00
|
|
|
[box setArrowEdge: newEdge];
|
2011-02-17 15:12:51 +03:00
|
|
|
}
|
|
|
|
|
2016-02-09 00:10:51 +03:00
|
|
|
- (ArrowEdge) arrowEdge
|
2011-02-17 15:12:51 +03:00
|
|
|
{
|
2016-02-09 00:10:51 +03:00
|
|
|
return [box arrowEdge];
|
2011-02-17 15:12:51 +03:00
|
|
|
}
|
|
|
|
|
2016-02-09 00:10:51 +03:00
|
|
|
- (void) setCornerRadius: (CGFloat)newRadius
|
2011-02-17 15:12:51 +03:00
|
|
|
{
|
2016-02-09 00:10:51 +03:00
|
|
|
[box setCornerRadius: newRadius];
|
2011-02-17 15:12:51 +03:00
|
|
|
}
|
|
|
|
|
2016-02-09 00:10:51 +03:00
|
|
|
- (CGFloat) cornerRadius
|
2011-02-17 15:12:51 +03:00
|
|
|
{
|
2016-02-09 00:10:51 +03:00
|
|
|
return [box cornerRadius];
|
2011-02-17 15:12:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|