2010-01-03 133 views
5

我正在嘗試重新編寫一個應用程序,我在Windows中使用Objective-C爲我的Mac創建了一個應用程序,並且我希望能夠像Mac的熱點角落。如果我將鼠標移動到屏幕的左側,它會使窗口可見,如果將它移動到窗口位置之外,窗口將再次隱藏。 (窗口將被推到屏幕的左側)。製作一個窗口彈出和跳出屏幕邊緣

有沒有人知道我在哪裏可以找到一些演示代碼(或參考)如何做到這一點,或至少如何判斷鼠標在哪裏,即使當前的應用程序不在最前面。 (不知道該怎麼說,也習慣了Windows世界)。

謝謝

-Brad

+0

這聽起來更象水銀一樣的貨架和剪貼板歷史記錄,和(可選)的Adium的聯繫人列表窗口,當鼠標點擊該邊緣可以彈出並在屏幕的一側。那是你在想什麼? – 2010-01-03 10:10:52

+0

是的,那是我在想什麼。 – Brad 2010-01-03 21:36:31

回答

1

這是我想出了。感謝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 
+0

甜,感謝您的演示代碼 – Brad 2010-05-02 21:00:34

+0

只是注意到我混合最小和最大的每個地方,但在-adjustWrapperView – 2010-05-05 17:26:58

+0

@PierreBernard你是否修復答案中的最小和最大值,還是它仍然是倒退..也沒有'@接口'對於這個'@ implementation',並不是_exactly_明確什麼需要在其中......例如,沒有'slidingEdge'被定義,等等。 – 2012-05-01 03:15:55

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

+0

拼圖的另一部分是讓窗口實際上彈出和彈出。您需要將窗口的內容視圖的直接子視圖放到另一個視圖中,然後製作* that *內容視圖。將view-within-the-content-view中的自動調整大小的掩碼設置爲不調整大小,並將其原點裝訂到內容視圖的左邊緣(在您的示例中)。要隱藏窗口,請將窗口大小調整爲1像素寬。要顯示它,請恢復其適當的大小。如果您希望窗口可調整大小,則需要在顯示/隱藏窗口後切換視圖的自動調整屏蔽。 – 2010-01-04 03:23:56

+0

你不會簡單地將窗口移入和移出的原因是因爲用戶可能在窗口移出的區域具有另一個屏幕。那麼,你並沒有隱藏窗口,只是將它移動到另一個屏幕上 - 而不是用戶想要的。 (至於用戶在這種情況下如何顯示窗口,用戶可以將窗口向上或向下移動到較小屏幕的邊緣,給他一些東西打。) – 2010-01-04 03:27:00

+0

我忘了覆蓋窗口的一些東西: [self setHasShadow:NO]; 和 [self setIgnoresMouseEvents:NO]; 因此您的隱形窗口實際上並不會阻止點擊。我99%確定你仍然會得到鼠標移動的事件。 – 2010-01-04 15:32:06

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