Ignore:
Timestamp:
09/20/09 04:54:21 (15 years ago)
Author:
Nicholas Riley
Message:

Better solution for volume popup in 10.6: use a menu.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Cocoa/Pester/Source/PSVolumeController.m

    r583 r584  
    1111#import "NJRNonCenteringWindow.h"
    1212
     13@interface NSMenu (SnowLeopardAdditions)
     14- (BOOL)popUpMenuPositioningItem:(NSMenuItem *)item atLocation:(NSPoint)location inView:(NSView *)view;
     15- (void)setAllowsContextMenuPlugIns:(BOOL)allows;
     16- (void)cancelTracking;
     17@end
     18
     19@interface NSMenuItem (SnowLeopardAdditions)
     20- (void)setView:(NSView *)view;
     21@end
     22
    1323@implementation PSVolumeController
    1424
     
    2232    if ( (self = [self initWithWindowNibName: @"Volume"]) != nil) {
    2333        [self window]; // connect outlets
    24         NSWindow *window = [[NJRNonCenteringWindow alloc] initWithContentRect: [contentView bounds] styleMask: NSBorderlessWindowMask backing: NSBackingStoreBuffered defer: NO];
    2534
    26         if ([NJRSoundManager volumeIsNotMutedOrInvalid: volume])
     35        if ([NJRSoundManager volumeIsNotMutedOrInvalid: volume])
    2736            [volumeSlider setFloatValue: volume];
    2837
    2938        delegate = [aDelegate retain];
    3039
    31         [window setContentView: contentView];
    32         [window setInitialFirstResponder: volumeSlider];
    33         [window makeFirstResponder: volumeSlider];
    34         [window setOpaque: NO];
    35         [window setBackgroundColor: [NSColor colorWithCalibratedWhite: 0.81f alpha: 0.9f]];
    36         [window setHasShadow: YES];
    37         [window setOneShot: YES];
    38         [window setDelegate: self];
    39         NSView *view = [aDelegate volumeControllerLaunchingView: self];
    40         if (view != nil) {
    41             NSRect rect = [view convertRect: [view bounds] toView: nil];
    42             NSWindow *parentWindow = [view window];
    43             rect.origin = [parentWindow convertBaseToScreen: rect.origin];
    44             rect.origin.x -= [window frame].size.width - rect.size.width + 1;
    45             [window setFrameTopLeftPoint: rect.origin];
    46             NSRect visibleFrame = [[parentWindow screen] visibleFrame];
    47             if (!NSContainsRect(visibleFrame, [window frame])) {
    48                 NSPoint viewTopLeft = { rect.origin.x, rect.origin.y + rect.size.height };
    49                 [window setFrameOrigin: viewTopLeft];
    50             }
    51         }
     40        NSView *view = [aDelegate volumeControllerLaunchingView: self];
    5241
    53         // -[NSApplication beginModalSessionForWindow:] shows and centers the window; we use NJRNonCenteringWindow to prevent the repositioning from succeeding
    54         NSModalSession session = [NSApp beginModalSessionForWindow: window];
    5542        // In 10.6, we can no longer force the modal session to work by "seeding" the slider with a mouse-down event.
    56         // Instead, we stop the modal session on a volume change.
    57         while ([NSApp runModalSession: session] == NSRunContinuesResponse) {
    58             // Any mouse click events that do not change the slider value should abort.
    59             NSEvent *event = [NSApp currentEvent];
    60             unsigned int eventTypeMask = NSEventMaskFromType([event type]);
    61             if (eventTypeMask & (NSLeftMouseDownMask | NSRightMouseDownMask | NSOtherMouseDownMask)) {
    62                 [NSApp preventWindowOrdering];
    63                 [NSApp discardEventsMatchingMask: NSAnyEventMask beforeEvent: event];
    64                 break;
     43        // Instead, use a menu.  (This should mostly work on 10.5 too, but is currently untested.)
     44        if ([NSMenu instancesRespondToSelector: @selector(popUpMenuPositioningItem:atLocation:inView:)]) {
     45            menu = [[NSMenu alloc] initWithTitle: @""];
     46            NSMenuItem *menuItem = [[NSMenuItem alloc] init];
     47            [menuItem setView: contentView];
     48            [menu addItem: menuItem];
     49            [menuItem release];
     50            NSPoint point;
     51            if (view != nil) {
     52                NSSize size = [view bounds].size;
     53                point = [view isFlipped] ? NSMakePoint(0, size.height) : NSZeroPoint;
     54            } else {
     55                point = [NSEvent mouseLocation];
    6556            }
    66             if (eventTypeMask & (NSKeyDownMask | NSKeyUpMask)) {
    67                 unsigned short keyCode = [event keyCode];
    68                 if (keyCode == 53 || keyCode == 36 || keyCode == 76) { // escape, return, enter
    69                     [NSApp discardEventsMatchingMask: NSAnyEventMask beforeEvent: event];
    70                     break;
     57            [menu setAllowsContextMenuPlugIns: NO];
     58            [menu popUpMenuPositioningItem: nil atLocation: point inView: view];
     59            [menu release];
     60        } else {
     61            NSWindow *window = [[NJRNonCenteringWindow alloc] initWithContentRect: [contentView bounds] styleMask: NSBorderlessWindowMask backing: NSBackingStoreBuffered defer: NO];
     62            [window setContentView: contentView];
     63            [window setOpaque: NO];
     64            [window setBackgroundColor: [NSColor colorWithCalibratedWhite: 0.81f alpha: 0.9f]];
     65            [window setHasShadow: YES];
     66            [window setOneShot: YES];
     67            [window setDelegate: self];
     68
     69            if (view != nil) {
     70                NSRect rect = [view convertRect: [view bounds] toView: nil];
     71                NSWindow *parentWindow = [view window];
     72                rect.origin = [parentWindow convertBaseToScreen: rect.origin];
     73                rect.origin.x -= [window frame].size.width - rect.size.width + 1;
     74                [window setFrameTopLeftPoint: rect.origin];
     75                NSRect visibleFrame = [[parentWindow screen] visibleFrame];
     76                if (!NSContainsRect(visibleFrame, [window frame])) {
     77                    NSPoint viewTopLeft = { rect.origin.x, rect.origin.y + rect.size.height };
     78                    [window setFrameOrigin: viewTopLeft];
    7179                }
    7280            }
     81            // -[NSApplication beginModalSessionForWindow:] shows and centers the window; we use NJRNonCenteringWindow to prevent the repositioning from succeeding
     82            NSModalSession session = [NSApp beginModalSessionForWindow: window];
     83            [volumeSlider mouseDown: [NSApp currentEvent]];
     84            [NSApp runModalSession: session];
     85            [NSApp endModalSession: session];
     86            [window close];
    7387        }
    74         [NSApp endModalSession: session];
    75         [window close];
     88
    7689        [self autorelease];
    77         // XXX make sure window and self are released
    7890    }
    7991    return self;
     
    89101{
    90102    [delegate volumeController: self didSetVolume: [sender floatValue]];
    91     [NSApp stopModal];
     103    if (NSEventMaskFromType([[NSApp currentEvent] type]) & (NSLeftMouseUpMask | NSRightMouseUpMask | NSOtherMouseUpMask))
     104        [menu cancelTracking];
    92105}
    93106
Note: See TracChangeset for help on using the changeset viewer.