使窗口弹出和滑出屏幕边缘

时间:2010-01-03 08:49:01

标签: objective-c cocoa macos

我正在尝试使用Objective-C为我的Mac重新编写我的Windows应用程序,我希望能够像Mac的热门角落那样做。如果我将鼠标移动到屏幕的左侧,它将显示一个窗口,如果我将它移到窗口位置之外,窗口将再次隐藏。 (窗口将被推到屏幕的左侧)。

有谁知道我在哪里可以找到关于如何做到这一点的演示代码(或参考),或者至少如何判断鼠标在哪里,即使当前应用程序不在顶部。 (不知道怎么说这个,太习惯了Windows世界)。

谢谢

-Brad

4 个答案:

答案 0 :(得分:2)

您将要在屏幕边缘实现一个不可见的窗口,并设置窗口顺序,以便它始终位于顶部。然后,您可以在此窗口中侦听鼠标移动的事件。

要将窗口设置为不可见且位于顶部,请使用以下调用创建窗口子类:

[self setBackgroundColor:[NSColor clearColor]];
[self setExcludedFromWindowsMenu:YES];
[self setCanHide:NO];
[self setLevel:NSScreenSaverWindowLevel];
[self setAlphaValue:0.0f];
[self setOpaque:NO];
[self orderFrontRegardless];

然后,打开鼠标移动的事件,

[self setAcceptsMouseMovedEvents:YES];

将导致窗口调用:

- (void)mouseMoved:(NSEvent *)theEvent
{
   NSLog(@"mouse moved into invisible window.");
}

所以希望这足以让你有个开始。

-Ken

答案 1 :(得分:2)

你可能会看看我们是如何在Visor项目中做到的: http://github.com/binaryage/visor/blob/master/src/Visor.m#L1025-1063

答案 2 :(得分:2)

这是AutoHidingWindow - SlidingWindow的子类,当鼠标到达屏幕边缘时弹出。欢迎反馈。

@interface ActivationWindow : NSWindow
{
    AutoHidingWindow *_activationDelegate;
    NSTrackingArea *_trackingArea;
}

- (ActivationWindow*)initWithDelegate:(AutoHidingWindow*)activationDelegate;

@property (assign) AutoHidingWindow *activationDelegate;
@property (retain) NSTrackingArea *trackingArea;

- (void)adjustWindowFrame;
- (void)adjustTrackingArea;

@end

@interface AutoHidingWindow ()

- (void)autoShow;
- (void)autoHide;

@end 


@implementation AutoHidingWindow

- (id)initWithContentRect:(NSRect) contentRect 
                styleMask:(unsigned int) styleMask 
                  backing:(NSBackingStoreType) backingType 
                    defer:(BOOL) flag
{

    if ((self = [super initWithContentRect:contentRect
                                 styleMask:NSBorderlessWindowMask 
                                   backing:backingType
                                     defer:flag])) {
        _activationWindow = [[ActivationWindow alloc] initWithDelegate:self];
    }

    return self;
}

@synthesize activationWindow = _activationWindow;

- (void)dealloc
{
    [_activationWindow release], _activationWindow = nil;

    [super dealloc];
}

- (void)makeKeyAndOrderFront:(id)sender
{
    [super makeKeyAndOrderFront:sender];

}

- (void)autoShow
{
    [self makeKeyAndOrderFront:self];

    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(autoHide) object:nil];
    [self performSelector:@selector(autoHide) withObject:nil afterDelay:2];
}

- (void)autoHide
{
    NSPoint mouseLocation = [NSEvent mouseLocation];
    NSRect windowFrame = [self frame];

    if (NSPointInRect(mouseLocation, windowFrame)) {
        [self performSelector:@selector(autoHide) withObject:nil afterDelay:2];
    }
    else {
        [self orderOut:self];
    }
}

@end


@implementation ActivationWindow 

- (ActivationWindow*)initWithDelegate:(AutoHidingWindow*)activationDelegate
{   
    if ((self = [super initWithContentRect:[[NSScreen mainScreen] frame]
                                 styleMask:NSBorderlessWindowMask
                                   backing:NSBackingStoreBuffered
                                     defer:NO]) != nil) {
        _activationDelegate = activationDelegate;

        [self setBackgroundColor:[NSColor clearColor]];
        [self setExcludedFromWindowsMenu:YES];
        [self setCanHide:NO];
        [self setHasShadow:NO];
        [self setLevel:NSScreenSaverWindowLevel];
        [self setAlphaValue:0.0];
        [self setIgnoresMouseEvents:YES];
        [self setOpaque:NO];
        [self orderFrontRegardless];

        [self adjustWindowFrame];
        [self.activationDelegate addObserver:self
                                 forKeyPath:@"slidingEdge"
                                    options:0
                                    context:@"slidingEdge"];
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(screenParametersChanged:) 
                                                     name:NSApplicationDidChangeScreenParametersNotification 
                                                   object:nil];     
    }

    return self;
}

@synthesize activationDelegate = _activationDelegate;
@synthesize trackingArea = _trackingArea;

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([@"slidingEdge" isEqual:context]) {
        [self adjustTrackingArea];
    }
    else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}


- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];

    [self.activationDelegate removeObserver:self forKeyPath:@"slidingEdge"];
    _activationDelegate = nil;

    [_trackingArea release], _trackingArea = nil;

    [super dealloc];
}

- (void)screenParametersChanged:(NSNotification *)notification
{
    [self adjustWindowFrame];
}

- (void)adjustWindowFrame
{
    NSScreen *mainScreen = [NSScreen mainScreen];
    CGFloat menuBarHeight = [NSMenuView menuBarHeight];
    NSRect windowFrame = [mainScreen frame];

    windowFrame.size.height -= menuBarHeight;

    [self setFrame:windowFrame display:NO];
    [self adjustTrackingArea];
}

- (void)adjustTrackingArea
{
    NSView *contentView = [self contentView];
    NSRect trackingRect = contentView.bounds;   
    CGRectEdge slidingEdge = self.activationDelegate.slidingEdge;
    CGFloat trackingRectSize = 2.0;

    switch (slidingEdge) {
        case CGRectMaxXEdge:
            trackingRect.origin.x = trackingRect.origin.x + trackingRect.size.width - trackingRectSize;
            trackingRect.size.width = trackingRectSize;
            break;

        case CGRectMaxYEdge:
            trackingRect.origin.y = trackingRect.origin.y + trackingRect.size.height - trackingRectSize;
            trackingRect.size.height = trackingRectSize;
            break;

        case CGRectMinXEdge:
            trackingRect.origin.x = 0;
            trackingRect.size.width = trackingRectSize;
            break;

        case CGRectMinYEdge:
        default:
            trackingRect.origin.y = 0;
            trackingRect.size.height = trackingRectSize;
    }


    NSTrackingAreaOptions options =
    NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved |
    NSTrackingActiveAlways |
    NSTrackingEnabledDuringMouseDrag;

    NSTrackingArea *trackingArea = self.trackingArea;

    if (trackingArea != nil) {
        [contentView removeTrackingArea:trackingArea];
    }

    trackingArea = [[NSTrackingArea alloc] initWithRect:trackingRect
                                                options:options
                                                  owner:self
                                               userInfo:nil];

    [contentView addTrackingArea:trackingArea];

    self.trackingArea = [trackingArea autorelease];
}

- (void)mouseEntered:(NSEvent *)theEvent
{
    [self.activationDelegate autoShow];
}


- (void)mouseMoved:(NSEvent *)theEvent
{
    [self.activationDelegate autoShow];
}

- (void)mouseExited:(NSEvent *)theEvent
{
}

@end

答案 3 :(得分:1)

这就是我想出的。感谢彼得的上述提示。

   @interface SlidingWindow : NSWindow
    {
        CGRectEdge _slidingEdge;
        NSView *_wrapperView;
    }


    @property (nonatomic, assign) CGRectEdge slidingEdge;
    @property (nonatomic, retain) NSView *wrapperView;

    -(id)initWithContentRect:(NSRect) contentRect 
                   styleMask:(unsigned int) styleMask 
                     backing:(NSBackingStoreType) backingType 
                       defer:(BOOL) flag;

    - (NSView*)wrapperViewWithFrame:(NSRect)bounds;

    - (BOOL)mayOrderOut;

    @end

    @interface SlidingWindow ()

    - (void)adjustWrapperView;

    - (void)setWindowWidth:(NSNumber*)width;
    - (void)setWindowHeight:(NSNumber*)height;

    @end


    @implementation SlidingWindow


@synthesize slidingEdge = _slidingEdge;
@synthesize wrapperView = _wrapperView;


- (id)initWithContentRect:(NSRect) contentRect 
                styleMask:(unsigned int) styleMask 
                  backing:(NSBackingStoreType) backingType 
                    defer:(BOOL) flag
{

    if ((self = [super initWithContentRect:contentRect
                                 styleMask:NSBorderlessWindowMask 
                                   backing:backingType
                                     defer:flag])) {
        /* May want to setup some other options, 
         like transparent background or something */

        [self setSlidingEdge:CGRectMaxYEdge];
        [self setHidesOnDeactivate:YES];
        [self setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces];
    }

    return self;
}

- (NSView*)wrapperViewWithFrame:(NSRect)bounds
{
    return [[[NSView alloc] initWithFrame:bounds] autorelease];
}

- (void)adjustWrapperView
{
    if (self.wrapperView == nil) {
        NSRect frame = [self frame];
        NSRect bounds = NSMakeRect(0, 0, frame.size.width, frame.size.height);
        NSView *wrapperView = [self wrapperViewWithFrame:bounds];
        NSArray *subviews =  [[[[self contentView] subviews] copy] autorelease];

        for (NSView *view in subviews) {
            [wrapperView addSubview:view];
        }

        [wrapperView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
        [[self contentView] addSubview:wrapperView];

        self.wrapperView = wrapperView;
    }

    switch (self.slidingEdge) {
        case CGRectMaxXEdge:
            [self.wrapperView setAutoresizingMask:(NSViewHeightSizable | NSViewMaxXMargin)];
            break;

        case CGRectMaxYEdge:
            [self.wrapperView setAutoresizingMask:(NSViewWidthSizable | NSViewMaxYMargin)];
            break;

        case CGRectMinXEdge:
            [self.wrapperView setAutoresizingMask:(NSViewHeightSizable | NSViewMinXMargin)];
            break;

        case CGRectMinYEdge:
        default:
            [self.wrapperView setAutoresizingMask:(NSViewWidthSizable | NSViewMinYMargin)];
    }
}

- (void)makeKeyAndOrderFront:(id)sender
{
    [self adjustWrapperView];

    if ([self isVisible]) {
        [super makeKeyAndOrderFront:sender];
    }
    else {
        NSRect screenRect = [[NSScreen menubarScreen] visibleFrame];
        NSRect windowRect = [self frame];

        CGFloat x;
        CGFloat y;
        NSRect startWindowRect;
        NSRect endWindowRect;

        switch (self.slidingEdge) {
            case CGRectMinXEdge:
                x = 0;
                y = (screenRect.size.height - windowRect.size.height) / 2 + screenRect.origin.y;
                startWindowRect = NSMakeRect(x - windowRect.size.width, y, 0, windowRect.size.height);
                break;

            case CGRectMinYEdge:
                x = (screenRect.size.width - windowRect.size.width) / 2 + screenRect.origin.x;
                y = 0;
                startWindowRect = NSMakeRect(x, y - windowRect.size.height, windowRect.size.width, 0);
                break;

            case CGRectMaxXEdge:
                x = screenRect.size.width - windowRect.size.width + screenRect.origin.x;
                y = (screenRect.size.height - windowRect.size.height) / 2 + screenRect.origin.y;
                startWindowRect = NSMakeRect(x + windowRect.size.width, y, 0, windowRect.size.height);
                break;

            case CGRectMaxYEdge:
            default:
                x = (screenRect.size.width - windowRect.size.width) / 2 + screenRect.origin.x;
                y = screenRect.size.height - windowRect.size.height + screenRect.origin.y;
                startWindowRect = NSMakeRect(x, y + windowRect.size.height, windowRect.size.width, 0);
        }

        endWindowRect = NSMakeRect(x, y, windowRect.size.width, windowRect.size.height);

        [self setFrame:startWindowRect display:NO animate:NO];

        [super makeKeyAndOrderFront:sender];

        [self setFrame:endWindowRect display:YES animate:YES];

        [self performSelector:@selector(makeResizable)
                   withObject:nil
                   afterDelay:1];
    }
}

- (void)makeResizable
{
    NSView *wrapperView = self.wrapperView;
    NSRect frame = [self frame];
    NSRect bounds = NSMakeRect(0, 0, frame.size.width, frame.size.height);

    [wrapperView setFrame:bounds];
    [wrapperView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
}

- (void)orderOut:(id)sender
{
    [self adjustWrapperView];

    NSRect startWindowRect = [self frame];
    NSRect endWindowRect;

    switch (self.slidingEdge) {
        case CGRectMinXEdge:
            endWindowRect = NSMakeRect(startWindowRect.origin.x, 
                                       startWindowRect.origin.y,
                                       0,
                                       startWindowRect.size.height);
            break;

        case CGRectMinYEdge:
            endWindowRect = NSMakeRect(startWindowRect.origin.x, 
                                       startWindowRect.origin.y, 
                                       startWindowRect.size.width, 
                                       0);
            break;

        case CGRectMaxXEdge:
            endWindowRect = NSMakeRect(startWindowRect.origin.x + startWindowRect.size.width, 
                                       startWindowRect.origin.y,
                                       0,
                                       startWindowRect.size.height);
            break;

        case CGRectMaxYEdge:
        default:
            endWindowRect = NSMakeRect(startWindowRect.origin.x, 
                                       startWindowRect.origin.y + startWindowRect.size.height, 
                                       startWindowRect.size.width, 
                                       0);
    }

    [self setFrame:endWindowRect display:YES animate:YES];

    switch (self.slidingEdge) {
        case CGRectMaxXEdge:
        case CGRectMinXEdge:
            if (startWindowRect.size.width > 0) {
                [self performSelector:@selector(setWindowWidth:)
                           withObject:[NSNumber numberWithDouble:startWindowRect.size.width]
                           afterDelay:0];
            }
            break;

        case CGRectMaxYEdge:
        case CGRectMinYEdge:
        default:
            if (startWindowRect.size.height > 0) {
                [self performSelector:@selector(setWindowHeight:)
                           withObject:[NSNumber numberWithDouble:startWindowRect.size.height]
                           afterDelay:0];
            }
    }

    [super orderOut:sender];
}

- (void)setWindowWidth:(NSNumber*)width
{
    NSRect startWindowRect = [self frame];
    NSRect endWindowRect = NSMakeRect(startWindowRect.origin.x, 
                                      startWindowRect.origin.y, 
                                      [width doubleValue],
                                      startWindowRect.size.height);

    [self setFrame:endWindowRect display:NO animate:NO];    
}

- (void)setWindowHeight:(NSNumber*)height
{
    NSRect startWindowRect = [self frame];
    NSRect endWindowRect = NSMakeRect(startWindowRect.origin.x, 
                                      startWindowRect.origin.y, 
                                      startWindowRect.size.width, 
                                      [height doubleValue]);

    [self setFrame:endWindowRect display:NO animate:NO];    
}

- (void)resignKeyWindow
{
    [self orderOut:self];

    [super resignKeyWindow];
}

- (BOOL)canBecomeKeyWindow
{
    return YES;
}

- (void)performClose:(id)sender
{
    [self close];
}

- (void)dealloc
{
    [_wrapperView release], _wrapperView = nil;

    [super dealloc];
}

@end


@implementation NSScreen (MenubarScreen)

+ (NSScreen*)menubarScreen
{
    NSArray *screens = [self screens];

    if ([screens count] > 0) {
        return [screens objectAtIndex:0];
    }

    return nil;
}
@end