Objective c 在屏幕边缘弹出一个窗口

Objective c 在屏幕边缘弹出一个窗口,objective-c,cocoa,macos,Objective C,Cocoa,Macos,我正在尝试用Objective-C为我的Mac电脑重新编写一个Windows应用程序,我希望能够做一些类似Mac电脑的热门角落的事情。如果我将鼠标移到屏幕左侧,它将使窗口可见,如果我将鼠标移到窗口位置之外,窗口将再次隐藏。(窗口将被推到屏幕左侧) 有人知道我在哪里可以找到一些演示代码(或参考资料)来说明如何做到这一点,或者至少知道如何知道鼠标在哪里,即使当前的应用程序不在顶部。(不知道该怎么说,也习惯于Windows世界) 多谢各位 -Brad您需要在屏幕边缘设置一个不可见窗口,并设置窗口顺序,

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

有人知道我在哪里可以找到一些演示代码(或参考资料)来说明如何做到这一点,或者至少知道如何知道鼠标在哪里,即使当前的应用程序不在顶部。(不知道该怎么说,也习惯于Windows世界)

多谢各位


-Brad

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

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

[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

您可以看看我们在Visor项目中是如何做到的:
以下是我的想法。感谢Peter提供的上述提示

   @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

这是AutoHidingWindow-滑动窗口的一个子类,当鼠标点击屏幕边缘时弹出。欢迎反馈

@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

这听起来更像是Quicksilver的书架和剪贴板历史记录窗口,以及(可选)Adium的联系人列表,当鼠标点击屏幕边缘时,可以在屏幕一侧弹出。这就是你在想的吗?是的,这就是我在想的。谜题的另一个部分是让窗户实际上弹出。您需要将窗口内容视图的直接子视图放到另一个视图中,并使其成为内容视图。将内容视图中视图的自动调整大小掩码设置为不调整大小,并将其原点固定到内容视图的左边缘(在您的示例中)。要隐藏窗口,请将窗口大小调整为1像素宽。要显示它,请恢复其正确大小。如果希望窗口的大小可以调整,则需要在显示/隐藏窗口后切换视图的自动调整大小掩码。之所以不简单地将窗口移入和移出,是因为用户可能在窗口将移出的区域中有另一个屏幕。然后,您没有隐藏窗口,只是将其移动到另一个屏幕,而不是用户想要的。(至于用户在这种情况下如何显示窗口,用户可以让窗口在小屏幕边缘上下移动,给他一些可以点击的东西。)覆盖窗口的一些东西我忘记了:[self-setHasShadow:NO];和[self-setIgnoresMouseEvents:否];因此,您的不可见窗口实际上不会阻止单击。我99%肯定你仍然会得到鼠标移动事件。为了补充Peter所说的,Cocoa不允许windows完全脱离屏幕。这就是为什么你不能简单地把它滑上滑下。然而,有一个棘手的解决办法。Cocoa在NSWindow的ConstraintFrameRect:toScreen:中执行限制。因此,如果您覆盖此例程,您可以使窗口脱离屏幕。这样做可能更容易,而无需实际调整窗口大小。不确定,谢谢大家的意见。。。我很感激。我注意到我在每个地方都混合了min和max,除了-adjustWrapperView@PierreBernard你在答案中修正了最小值和最大值,还是仍然是向后的。。此外,对于这个
@实现
,没有
@接口
,也不清楚需要在其中包含什么…例如,没有定义任何
滑动边
,等等@alexgray I现在已经用正确的最小/最大值SCOOL更新了代码。我唯一要注意的是
+(NSScreen*)菜单屏幕
的类别。。我想说这是不需要的,因为您可以从文档中简单地调用
[NSScreen mainScreen]
,no:“主屏幕不一定是包含菜单栏或其原点位于(0,0)的同一屏幕。主屏幕是指包含当前接收键盘事件的窗口的屏幕。它是主屏幕,因为它是用户最有可能与之交互的屏幕。“此项目发生了什么?链接已断开。”。看不出相关性。