diff --git a/AppKit/CPMenu/CPMenu.j b/AppKit/CPMenu/CPMenu.j index 70017bcfe..765ccdde4 100644 --- a/AppKit/CPMenu/CPMenu.j +++ b/AppKit/CPMenu/CPMenu.j @@ -25,6 +25,7 @@ @import @import +@import "_CPMenuManager.j" @import "CPApplication.j" @import "CPClipView.j" @import "CPMenuItem.j" @@ -40,7 +41,7 @@ CPMenuDidRemoveItemNotification = @"CPMenuDidRemoveItemNotification"; CPMenuDidEndTrackingNotification = @"CPMenuDidEndTrackingNotification"; -var MENUBAR_HEIGHT = 19.0; +var MENUBAR_HEIGHT = 28.0; var _CPMenuBarVisible = NO, _CPMenuBarTitle = @"", @@ -226,7 +227,7 @@ var _CPMenuBarVisible = NO, - (float)menuBarHeight { - if (self == [CPApp mainMenu]) + if (self === [CPApp mainMenu]) return MENUBAR_HEIGHT; return 0.0; @@ -639,6 +640,14 @@ var _CPMenuBarVisible = NO, } // ++ (CGRect)_constraintRectForView:(CPView)aView +{ + if ([CPPlatform isBrowser]) + return CGRectInset([[[aView window] platformWindow] contentBounds], 5.0, 5.0); + + return CGRectInset([[[aView window] screen] visibleFrame], 5.0, 5.0); +} + - (void)popUpMenuPositioningItem:(CPMenuItem)anItem atLocation:(CGPoint)aLocation inView:(CPView)aView callback:(Function)aCallback { var itemIndex = [self indexOfItem:anItem]; @@ -679,16 +688,25 @@ var aFont = nil; if ([delegate respondsToSelector:@selector(menuWillOpen:)]) [delegate menuWillOpen:aMenu]; + var constraintRect = [CPMenu _constraintRectForView:aView]; + + [menuWindow setConstraintRect:constraintRect]; [menuWindow orderFront:self]; - [menuWindow - beginTrackingWithEvent:[CPApp currentEvent] - callback:[CPMenu trackingCallbackWithCallback:aCallback]]; + + [[_CPMenuManager sharedMenuManager] + beginTracking:[CPApp currentEvent] + menuContainer:menuWindow + constraintRect:constraintRect + callback:[CPMenu trackingCallbackWithCallback:aCallback]]; } + (Function)trackingCallbackWithCallback:(Function)aCallback { return function(aMenuWindow, aMenu) { + [aMenuWindow setMenu:nil]; + [aMenuWindow orderOut:self]; + [_CPMenuWindow poolMenuWindow:aMenuWindow]; if (aCallback) @@ -731,12 +749,18 @@ var aFont = nil; [menuWindow setDelegate:self]; [menuWindow setBackgroundStyle:isForMenuBar ? _CPMenuWindowMenuBarBackgroundStyle : _CPMenuWindowPopUpBackgroundStyle]; - [menuWindow setFrameOrigin:[[anEvent window] convertBaseToGlobal:[anEvent locationInWindow]]]; + var constraintRect = [CPMenu _constraintRectForView:aView]; + + [menuWindow setConstraintRect:constraintRect]; + [menuWindow setFrameOrigin:[[anEvent window] convertBaseToGlobal:[anEvent locationInWindow]]]; [menuWindow orderFront:self]; - [menuWindow - beginTrackingWithEvent:anEvent - callback:[CPMenu trackingCallbackWithCallback:nil]]; + + [[_CPMenuManager sharedMenuManager] + beginTracking:anEvent + menuContainer:menuWindow + constraintRect:[CPMenu _constraintRectForView:aView] + callback:[CPMenu trackingCallbackWithCallback:nil]]; } // Managing Display of State Column diff --git a/AppKit/CPMenu/_CPMenuBarWindow.j b/AppKit/CPMenu/_CPMenuBarWindow.j index 5594613ee..786c7e149 100644 --- a/AppKit/CPMenu/_CPMenuBarWindow.j +++ b/AppKit/CPMenu/_CPMenuBarWindow.j @@ -316,150 +316,22 @@ var _CPMenuBarWindowBackgroundColor = nil, [self tile]; } -- (CGRect)frameForMenuItem:(CPMenuItem)aMenuItem -{ - var frame = [[aMenuItem _menuItemView] frame]; - - frame.origin.x -= 5.0; - frame.origin.y = 0; - frame.size.width += 10.0; - frame.size.height = MENUBAR_HEIGHT; - - return frame; -} - -- (CPMenuItem)menuItemAtPoint:(CGPoint)aPoint -{ - var items = [_menu itemArray], - count = items.length; - - while (count--) - { - var item = items[count]; - - if ([item isHidden] || [item isSeparatorItem]) - continue; - - if (CGRectContainsPoint([self frameForMenuItem:item], aPoint)) - return item; - } - - return nil; -} - - (void)mouseDown:(CPEvent)anEvent { - _trackingMenuItem = [self menuItemAtPoint:[anEvent locationInWindow]]; - - if (![_trackingMenuItem isEnabled]) - return; - - if ([[_trackingMenuItem _menuItemView] eventOnSubmenu:anEvent]) - return [self showMenu:anEvent]; - - if ([_trackingMenuItem isEnabled]) - [self trackEvent:anEvent]; + var constraintRect = CGRectInset([[self platformWindow] visibleFrame], 5.0, 0.0); + + constraintRect.size.height -= 5.0; + + [[_CPMenuManager sharedMenuManager] + beginTracking:anEvent + menuContainer:self + constraintRect:constraintRect + callback:nil]; } -- (void)trackEvent:(CPEvent)anEvent +- (CPFont)font { - var type = [anEvent type]; - - if (type === CPPeriodic) - return [self showMenu:anEvent]; - - var frame = [self frameForMenuItem:_trackingMenuItem], - menuItemView = [_trackingMenuItem _menuItemView], - onMenuItemView = CGRectContainsPoint(frame, [anEvent locationInWindow]); - - if (type == CPLeftMouseDown) - { - if ([_trackingMenuItem submenu] != nil) - { - var action = [_trackingMenuItem action]; - - // If the item has a submenu, but not direct action, a.k.a. a "pure" menu, simply show the menu. - // FIXME: (?) should we use submenuAction: or not? - if (!action || action === @selector(submenuAction:)) - return [self showMenu:anEvent]; - - // If this is a hybrid button/menu, show it in a bit... - [CPEvent startPeriodicEventsAfterDelay:0.0 withPeriod:0.5]; - } - - [menuItemView highlight:onMenuItemView]; - } - - else if (type == CPLeftMouseDragged) - { - if (!onMenuItemView && [_trackingMenuItem submenu]) - return [self showMenu:anEvent]; - - [menuItemView highlight:onMenuItemView]; - } - - else /*if (type == CPLeftMouseUp)*/ - { - [CPEvent stopPeriodicEvents]; - - [menuItemView highlight:NO]; - - if (onMenuItemView) - [CPApp sendAction:[_trackingMenuItem action] to:[_trackingMenuItem target] from:_trackingMenuItem]; - - return; - } - - [CPApp setTarget:self selector:@selector(trackEvent:) forNextEventMatchingMask:CPPeriodicMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES]; -} - -- (void)showMenu:(CPEvent)anEvent -{ - [CPEvent stopPeriodicEvents]; - - var frame = [self frameForMenuItem:_trackingMenuItem], - menuItemView = [_trackingMenuItem _menuItemView]; - - if (!_highlightView) - { - _highlightView = [[CPView alloc] initWithFrame:frame]; - - [_highlightView setBackgroundColor:_highlightColor ? _highlightColor : [CPColor colorWithRed:95.0/255.0 green:131.0/255.0 blue:185.0/255.0 alpha:1.0]]; - } - else - [_highlightView setFrame:frame]; - - [[self contentView] addSubview:_highlightView positioned:CPWindowBelow relativeTo:menuItemView]; - - [menuItemView activate:YES]; - - var submenu = [_trackingMenuItem submenu]; - - [[CPNotificationCenter defaultCenter] - addObserver:self - selector:@selector(menuDidEndTracking:) - name:CPMenuDidEndTrackingNotification - object:submenu]; - - [CPMenu _popUpContextMenu:submenu - withEvent:[CPEvent mouseEventWithType:CPLeftMouseDown location:CGPointMake(CGRectGetMinX(frame), CGRectGetMaxY(frame)) - modifierFlags:[anEvent modifierFlags] timestamp:[anEvent timestamp] windowNumber:[self windowNumber] - context:nil eventNumber:0 clickCount:[anEvent clickCount] pressure:[anEvent pressure]] - forView:[self contentView] - withFont:nil - forMenuBar:YES]; -} - -- (void)menuDidEndTracking:(CPNotification)aNotification -{ - [_highlightView removeFromSuperview]; - - [[_trackingMenuItem _menuItemView] activate:NO]; - - [[CPNotificationCenter defaultCenter] - removeObserver:self - name:CPMenuDidEndTrackingNotification - object:[aNotification object]]; + [CPFont systemFontOfSize:12.0]; } - (void)tile @@ -492,13 +364,13 @@ var _CPMenuBarWindowBackgroundColor = nil, if (isLeftAligned) { - [menuItemView setFrameOrigin:CGPointMake(x, (MENUBAR_HEIGHT - 1.0 - CGRectGetHeight(frame)) / 2.0)]; - + [menuItemView setFrame:CGRectMake(x, 0.0, CGRectGetWidth(frame), MENUBAR_HEIGHT)]; + x += CGRectGetWidth([menuItemView frame]) + MENUBAR_MARGIN; } else { - [menuItemView setFrameOrigin:CGPointMake(x - CGRectGetWidth(frame), (MENUBAR_HEIGHT - 1.0 - CGRectGetHeight(frame)) / 2.0)]; + [menuItemView setFrame:CGRectMake(x - CGRectGetWidth(frame), 0.0, CGRectGetWidth(frame), MENUBAR_HEIGHT)]; x = CGRectGetMinX([menuItemView frame]) - MENUBAR_MARGIN; } @@ -532,8 +404,47 @@ var _CPMenuBarWindowBackgroundColor = nil, @end -@implementation __CPMenuBarWindow : _CPMenuWindow +@implementation _CPMenuBarWindow (CPMenuContainer) + +- (BOOL)isMenuBar { + return YES; +} + +- (CGRect)globalFrame +{ + return [self frame]; +} + +- (_CPManagerScrollingState)scrollingStateForPoint:(CGPoint)aGlobalLocation +{ + return _CPMenuManagerScrollingStateNone; +} + +- (CPInteger)itemIndexAtPoint:(CGPoint)aPoint +{ + var items = [_menu itemArray], + index = items.length; + + while (index--) + { + var item = items[index]; + + if ([item isHidden] || [item isSeparatorItem]) + continue; + + if (CGRectContainsPoint([self rectForItemAtIndex:index], aPoint)) + return index; + } + + return CPNotFound; +} + +- (CGRect)rectForItemAtIndex:(CPInteger)anIndex +{ + var menuItem = [_menu itemAtIndex:anIndex === CPNotFound ? 0 : anIndex]; + + return [[menuItem _menuItemView] frame]; } @end diff --git a/AppKit/CPMenu/_CPMenuManager.j b/AppKit/CPMenu/_CPMenuManager.j new file mode 100644 index 000000000..47ad9d6d2 --- /dev/null +++ b/AppKit/CPMenu/_CPMenuManager.j @@ -0,0 +1,299 @@ + +#include "../CoreGraphics/CGGeometry.h" + +@import + + +_CPMenuManagerScrollingStateUp = -1, +_CPMenuManagerScrollingStateDown = 1, +_CPMenuManagerScrollingStateNone = 0; + +var STICKY_TIME_INTERVAL = 500; + +var SharedMenuManager = nil; + +@implementation _CPMenuManager: CPObject +{ + CPTimeInterval _startTime; + int _scrollingState; + CGPoint _lastGlobalLocation; + + CPView _lastMouseOverMenuView; + + CGRect _constraintRect; + CPMutableArray _menuContainerStack; + + Function _trackingCallback; +} + ++ (_CPMenuManager)sharedMenuManager +{ + if (!SharedMenuManager) + SharedMenuManager = [[_CPMenuManager alloc] init]; + + return SharedMenuManager; +} + +- (id)init +{ + if (SharedMenuManager) + return SharedMenuManager; + + return [super init]; +} + +- (id)trackingMenuContainer +{ + return _menuContainerStack[0]; +} + +- (CPMenu)trackingMenu +{ + return [[self trackingMenuContainer] menu]; +} + +- (void)beginTracking:(CPEvent)anEvent + menuContainer:(id)aMenuContainer + constraintRect:(CGRect)aRect + callback:(Function)aCallback +{ + CPApp._activeMenu = [aMenuContainer menu]; + + _startTime = [anEvent timestamp];//new Date(); + _scrollingState = _CPMenuManagerScrollingStateNone; + + _constraintRect = aRect; + _trackingCallback = aCallback; + _menuContainerStack = [aMenuContainer]; + + [self trackEvent:anEvent]; +} + +- (void)trackEvent:(CPEvent)anEvent +{ + var type = [anEvent type], + trackingMenu = [self trackingMenu]; + + // Close Menu Event. + if (type === CPAppKitDefined) + { + // Stop all periodic events at this point. + [CPEvent stopPeriodicEvents]; + + // Get the original menu. + var menu = [self trackingMenu], + highlightedItem = [menu highlightedItem]; + + // Hide all submenus. + [self showMenu:nil fromMenu:menu atPoint:nil]; + + var delegate = [menu delegate]; + + if ([delegate respondsToSelector:@selector(menuDidClose:)]) + [delegate menuDidClose:menu]; + + if (_trackingCallback) + _trackingCallback([self trackingMenuContainer], menu); + + [[CPNotificationCenter defaultCenter] + postNotificationName:CPMenuDidEndTrackingNotification + object:menu]; + + CPApp._activeMenu = nil; + + return; + } + + [CPApp setTarget:self selector:@selector(trackEvent:) forNextEventMatchingMask:CPPeriodicMask | CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask | CPAppKitDefinedMask untilDate:nil inMode:nil dequeue:YES]; + + // Periodic events don't have a valid location. + var globalLocation = type === CPPeriodic ? _lastGlobalLocation : [anEvent globalLocation]; + + // Remember this for the next periodic event. + _lastGlobalLocation = globalLocation; + + // Find which menu window the mouse is currently on top of + var activeMenuContainer = [self menuContainerForPoint:globalLocation], + menuLocation = [activeMenuContainer convertGlobalToBase:globalLocation], + activeItemIndex = activeMenuContainer ? [activeMenuContainer itemIndexAtPoint:menuLocation] : CPNotFound, + activeMenu = activeMenuContainer ? [activeMenuContainer menu] : nil, + activeItem = activeMenuContainer ? [activeMenu itemAtIndex:activeItemIndex] : nil, + mouseOverMenuView = activeMenuContainer ? [activeItem view] : nil; + + if (type === CPPeriodic) + { + if (_scrollingState === _CPMenuManagerScrollingStateUp) + [activeMenuContainer scrollUp]; + + else if (_scrollingState === _CPMenuManagerScrollingStateDown) + [activeMenuContainer scrollDown]; + } + + // If we're over a custom menu view... + if (mouseOverMenuView) + { + if (!_lastMouseOverMenuView) + [menu _highlightItemAtIndex:CPNotFound]; + + if (_lastMouseOverMenuView != mouseOverMenuView) + { + [mouseOverMenuView mouseExited:anEvent]; + // FIXME: Possibly multiple of these? + [_lastMouseOverMenuView mouseEntered:anEvent]; + + _lastMouseOverMenuView = mouseOverMenuView; + } + + [self sendEvent:[CPEvent mouseEventWithType:type location:menuLocation modifierFlags:[anEvent modifierFlags] + timestamp:[anEvent timestamp] windowNumber:[self windowNumber] context:nil + eventNumber:0 clickCount:[anEvent clickCount] pressure:[anEvent pressure]]]; + } + else + { + if (_lastMouseOverMenuView) + { + [_lastMouseOverMenuView mouseExited:anEvent]; + _lastMouseOverMenuView = nil; + } + + [activeMenu _highlightItemAtIndex:activeItemIndex]; + + if (type === CPMouseMoved || type === CPLeftMouseDragged || type === CPLeftMouseDown) + { + var oldScrollingState = _scrollingState; + + _scrollingState = [activeMenuContainer scrollingStateForPoint:globalLocation]; + + if (_scrollingState !== oldScrollingState) + { + if (_scrollingState === _CPMenuManagerScrollingStateNone) + [CPEvent stopPeriodicEvents]; + + else if (oldScrollingState === _CPMenuManagerScrollingStateNone) + [CPEvent startPeriodicEventsAfterDelay:0.0 withPeriod:0.04]; + } + } + else if (type === CPLeftMouseUp && ([anEvent timestamp] - _startTime > STICKY_TIME_INTERVAL)) + [[self trackingMenu] cancelTracking]; + } + + // If the item has a submenu, show it. + if ([activeItem hasSubmenu])// && [activeItem action] === @selector(submenuAction:)) + { + var activeItemRect = [activeMenuContainer rectForItemAtIndex:activeItemIndex]; + + if ([activeMenuContainer isMenuBar]) + var newMenuOrigin = CGPointMake(CGRectGetMinX(activeItemRect), CGRectGetMaxY(activeItemRect)); + else + var newMenuOrigin = CGPointMake(CGRectGetMaxX(activeItemRect), CGRectGetMinY(activeItemRect)); + + newMenuOrigin = [activeMenuContainer convertBaseToGlobal:newMenuOrigin]; + + [self showMenu:[activeItem submenu] fromMenu:[activeItem menu] atPoint:newMenuOrigin]; + } + + // This handles both the case where we've moved away from the menu, and where + // we've moved to an item without a submenu. + else + [self showMenu:nil fromMenu:activeMenu atPoint:CGPointMakeZero()]; +} + + +- (id)menuContainerForPoint:(float)aGlobalLocation +{ + var count = [_menuContainerStack count], + firstMenuContainer = _menuContainerStack[0]; + + // Trivial case. + if (count === 1) + return firstMenuContainer; + + var firstMenuWindowIndex = 0, + lastMenuWindowIndex = count - 1; + + // If the first menu container is a bar, then it has to fall strictly within its bounds. + if ([firstMenuContainer isMenuBar]) + { + if (CGRectContainsPoint([firstMenuContainer globalFrame], aGlobalLocation)) + return firstMenuContainer; + + firstMenuWindowIndex = 1; + } + + var index = count, + x = aGlobalLocation.x, + closerDeltaX = Infinity, + closerMenuContainer = nil; + + while (index-- > firstMenuWindowIndex) + { + var menuContainer = _menuContainerStack[index], + menuContainerFrame = [menuContainer globalFrame], + menuContainerMinX = _CGRectGetMinX(menuContainerFrame), + menuContainerMaxX = _CGRectGetMaxX(menuContainerFrame); + + // If within the x bounds of this menu container, return it. + if (x < menuContainerMaxX && x >= menuContainerMinX) + return menuContainer; + + // If this is either the first or last menu *window*, check to see how close we are to it. + if (index === firstMenuWindowIndex || index === lastMenuWindowIndex) + { + var deltaX = ABS(x < menuContainerMinX ? menuContainerMinX - x : menuContainerMaxX - x); + + if (deltaX < closerDeltaX) + { + closerMenuContainer = menuContainer; + closerDeltaX = deltaX; + } + } + } + + return closerMenuContainer; +} + +- (void)showMenu:(CPMenu)newMenu fromMenu:(CPMenu)baseMenu atPoint:(CGPoint)aGlobalLocation +{//console.log("SM: " + [self trackingMenu] + " SHOW MENU: " + newMenu + " from menu " + baseMenu); + var count = _menuContainerStack.length, + index = count; + + // Hide all menus up to the base menu... + while (index--) + { + var menuContainer = _menuContainerStack[index]; + + if ([menuContainer menu] === baseMenu) + break; + + [[menuContainer menu] _highlightItemAtIndex:CPNotFound]; + + [menuContainer orderOut:self]; + [menuContainer setMenu:nil]; + + [_CPMenuWindow poolMenuWindow:menuContainer]; + [_menuContainerStack removeObjectAtIndex:index]; + } + + if (!newMenu) + return; + + // Unhighlight any previously highlighted item. + [newMenu _highlightItemAtIndex:CPNotFound]; + + var menuWindow = [_CPMenuWindow menuWindowWithMenu:newMenu font:[_menuContainerStack[0] font]]; + + [_menuContainerStack addObject:menuWindow]; + + [menuWindow setConstraintRect:_constraintRect]; + + // If our parent menu is a menu bar... + if (baseMenu === [self trackingMenu] && [[self trackingMenuContainer] isMenuBar]) + [menuWindow setBackgroundStyle:_CPMenuWindowMenuBarBackgroundStyle]; + else + [menuWindow setBackgroundStyle:_CPMenuWindowPopUpBackgroundStyle]; + + [menuWindow setFrameOrigin:aGlobalLocation]; + [menuWindow orderFront:self]; +} + +@end diff --git a/AppKit/CPMenu/_CPMenuWindow.j b/AppKit/CPMenu/_CPMenuWindow.j index e380a4d7a..f6291b45b 100644 --- a/AppKit/CPMenu/_CPMenuWindow.j +++ b/AppKit/CPMenu/_CPMenuWindow.j @@ -5,11 +5,7 @@ var _CPMenuWindowPool = [], _CPMenuWindowPoolCapacity = 5, - _CPMenuWindowBackgroundColors = [], - - _CPMenuWindowScrollingStateUp = -1, - _CPMenuWindowScrollingStateDown = 1, - _CPMenuWindowScrollingStateNone = 0; + _CPMenuWindowBackgroundColors = []; _CPMenuWindowMenuBarBackgroundStyle = 0; _CPMenuWindowPopUpBackgroundStyle = 1; @@ -31,21 +27,12 @@ var STICKY_TIME_INTERVAL = 500, { _CPMenuView _menuView; CPClipView _menuClipView; - CPView _lastMouseOverMenuView; CPImageView _moreAboveView; CPImageView _moreBelowView; - - CPTimeInterval _startTime; - int _scrollingState; - CGPoint _lastGlobalLocation; - - Function _trackingCallback; CGRect _unconstrainedFrame; CGRect _constraintRect; - - CPArray _menuWindowStack; } + (id)menuWindowWithMenu:(CPMenu)aMenu font:(CPFont)aFont @@ -126,6 +113,11 @@ var STICKY_TIME_INTERVAL = 500, return self; } +- (BOOL)canScroll +{ + return ![_moreAboveView isHidden] || ![_moreBelowView isHidden]; +} + - (CGFloat)overlapOffsetWidth { return LEFT_MARGIN; @@ -212,16 +204,6 @@ var STICKY_TIME_INTERVAL = 500, [self setFrameSize:CGSizeMake(MAX(size.width, aWidth), size.height)]; } -- (CGPoint)rectForItemAtIndex:(int)anIndex -{ - return [_menuView convertRect:[_menuView rectForItemAtIndex:anIndex] toView:nil]; -} - -- (int)itemIndexAtPoint:(CGPoint)aPoint -{ - return [_menuView itemIndexAtPoint:[_menuView convertPoint:aPoint fromView:nil]]; -} - - (CPMenu)menu { return [_menuView menu]; @@ -241,26 +223,6 @@ var STICKY_TIME_INTERVAL = 500, [self setFrame:_unconstrainedFrame]; } -- (void)scrollUp -{ - if (CGRectGetMinY(_unconstrainedFrame) >= CGRectGetMinY(_constraintRect)) - return; - - _unconstrainedFrame.origin.y += 10; - - [self setFrame:_unconstrainedFrame]; -} - -- (void)scrollDown -{ - if (CGRectGetMaxY(_unconstrainedFrame) <= CGRectGetHeight(_constraintRect)) - return; - - _unconstrainedFrame.origin.y -= 10; - - [self setFrame:_unconstrainedFrame]; -} - - (CGRect)unconstrainedFrame { return _unconstrainedFrame; @@ -341,232 +303,66 @@ var STICKY_TIME_INTERVAL = 500, [_menuView scrollPoint:CGPointMake(0.0, [self convertBaseToGlobal:clipFrame.origin].y - menuViewOrigin.y)]; } -- (void)beginTrackingWithEvent:(CPEvent)anEvent callback:(Function)aCallback +- (void)scrollUp { - CPApp._activeMenu = [self menu]; - - _startTime = [anEvent timestamp];//new Date(); - _scrollingState = _CPMenuWindowScrollingStateNone; - _menuWindowStack = [self]; - - _trackingCallback = aCallback; - - // bleh - [self setConstraintRect:CGRectInset([CPPlatform isBrowser] ? [[self platformWindow] contentBounds] : [[self screen] visibleFrame], 5.0, 5.0)]; - - [self trackEvent:anEvent]; -} - -- (_CPMenuWindow)menuWindowForPoint:(float)aGlobalLocation -{ - var count = [_menuWindowStack count]; - - // Trivial case. - if (count === 1) - return _menuWindowStack[0]; - - var index = count, - x = aGlobalLocation.x, - closerDeltaX = Infinity, - closerMenuWindow = nil; - - while (index--) - { - var menuWindow = _menuWindowStack[index], - menuWindowFrame = [menuWindow frame], - menuWindowMinX = _CGRectGetMinX(menuWindowFrame), - menuWindowMaxX = _CGRectGetMaxX(menuWindowFrame); - - // If within the x bounds of this menu window, return it. - if (x < menuWindowMaxX && x >= menuWindowMinX) - return menuWindow; - - // If this is either the first or last menu, check to see how close we are to it. - if (index === 0 || index === count - 1) - { - var deltaX = ABS(x < menuWindowMinX ? menuWindowMinX - x : menuWindowMaxX - x); - - if (deltaX < closerDeltaX) - { - closerMenuWindow = menuWindow; - closerDeltaX = deltaX; - } - } - } - - return closerMenuWindow; -} - -- (void)showMenu:(CPMenu)newMenu fromMenu:(CPMenu)baseMenu atPoint:(CGPoint)aGlobalLocation -{ - var count = _menuWindowStack.length, - index = count; - - // Hide all menus up to the base menu... - while (index--) - { - var menuWindow = _menuWindowStack[index]; - - if ([menuWindow menu] === baseMenu) - break; - - [[menuWindow menu] _highlightItemAtIndex:CPNotFound]; - - [menuWindow orderOut:self]; - [menuWindow setMenu:nil]; - - [_CPMenuWindow poolMenuWindow:menuWindow]; - [_menuWindowStack removeObjectAtIndex:index]; - } - - if (!newMenu) + if (CGRectGetMinY(_unconstrainedFrame) >= CGRectGetMinY(_constraintRect)) return; - // Unhighlight any previously highlighted item. - [newMenu _highlightItemAtIndex:CPNotFound]; + _unconstrainedFrame.origin.y += 10; - var menuWindow = [_CPMenuWindow menuWindowWithMenu:newMenu font:[self font]]; - - _menuWindowStack.push(menuWindow); - - [menuWindow setConstraintRect:_constraintRect]; - [menuWindow setBackgroundStyle:_CPMenuWindowPopUpBackgroundStyle]; - [menuWindow setFrameOrigin:aGlobalLocation]; - [menuWindow orderFront:self]; + [self setFrame:_unconstrainedFrame]; } -- (void)trackEvent:(CPEvent)anEvent +- (void)scrollDown { - var type = [anEvent type], - menu = [self menu]; - - // Close Menu Event. - if (type === CPAppKitDefined) - { - // Stop all periodic events at this point. - [CPEvent stopPeriodicEvents]; - - var highlightedItem = [menu highlightedItem]; - - // Hide all submenus. - [self showMenu:nil fromMenu:menu atPoint:nil]; - - [self orderOut:self]; - [self setMenu:nil]; - - var delegate = [menu delegate]; - - if ([delegate respondsToSelector:@selector(menuDidClose:)]) - [delegate menuDidClose:menu]; - - if (_trackingCallback) - _trackingCallback(self, menu); - - [[CPNotificationCenter defaultCenter] - postNotificationName:CPMenuDidEndTrackingNotification - object:menu]; - - CPApp._activeMenu = nil; - + if (CGRectGetMaxY(_unconstrainedFrame) <= CGRectGetHeight(_constraintRect)) return; - } - [CPApp setTarget:self selector:@selector(trackEvent:) forNextEventMatchingMask:CPPeriodicMask | CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask | CPAppKitDefinedMask untilDate:nil inMode:nil dequeue:YES]; + _unconstrainedFrame.origin.y -= 10; - // Periodic events don't have a valid location. - var globalLocation = type === CPPeriodic ? _lastGlobalLocation : [anEvent globalLocation]; + [self setFrame:_unconstrainedFrame]; +} - // Remember this for the next periodic event. - _lastGlobalLocation = globalLocation; +@end - // Find which menu window the mouse is currently on top of - var activeMenuWindow = [self menuWindowForPoint:globalLocation], - menuLocation = [activeMenuWindow convertGlobalToBase:globalLocation], - activeItemIndex = activeMenuWindow ? [activeMenuWindow itemIndexAtPoint:menuLocation] : CPNotFound, - activeMenu = activeMenuWindow ? [activeMenuWindow menu] : nil, - activeItem = activeMenuWindow ? [activeMenu itemAtIndex:activeItemIndex] : nil, - mouseOverMenuView = activeMenuWindow ? [activeItem view] : nil; +@implementation _CPMenuWindow (CPMenuContainer) - if (type === CPPeriodic) - { - if (_scrollingState === _CPMenuWindowScrollingStateUp) - [activeMenuWindow scrollUp]; +- (CGRect)globalFrame +{ + return [self frame]; +} - else if (_scrollingState === _CPMenuWindowScrollingStateDown) - [activeMenuWindow scrollDown]; - } +- (BOOL)isMenuBar +{ + return NO; +} - // If we're over a custom menu view... - if (mouseOverMenuView) - { - if (!_lastMouseOverMenuView) - [menu _highlightItemAtIndex:CPNotFound]; - - if (_lastMouseOverMenuView != mouseOverMenuView) - { - [mouseOverMenuView mouseExited:anEvent]; - // FIXME: Possibly multiple of these? - [_lastMouseOverMenuView mouseEntered:anEvent]; - - _lastMouseOverMenuView = mouseOverMenuView; - } - - [self sendEvent:[CPEvent mouseEventWithType:type location:menuLocation modifierFlags:[anEvent modifierFlags] - timestamp:[anEvent timestamp] windowNumber:[self windowNumber] context:nil - eventNumber:0 clickCount:[anEvent clickCount] pressure:[anEvent pressure]]]; - } - else - { - if (_lastMouseOverMenuView) - { - [_lastMouseOverMenuView mouseExited:anEvent]; - _lastMouseOverMenuView = nil; - } - - [activeMenu _highlightItemAtIndex:activeItemIndex]; - - if (type === CPMouseMoved || type === CPLeftMouseDragged || type === CPLeftMouseDown) - { - var frame = [self frame], - oldScrollingState = _scrollingState; - - _scrollingState = _CPMenuWindowScrollingStateNone; - - // If we're at or above of the top scroll indicator... - if (globalLocation.y < CGRectGetMinY(frame) + TOP_MARGIN + SCROLL_INDICATOR_HEIGHT) - _scrollingState = _CPMenuWindowScrollingStateUp; - - // If we're at or below the bottom scroll indicator... - else if (globalLocation.y > CGRectGetMaxY(frame) - BOTTOM_MARGIN - SCROLL_INDICATOR_HEIGHT) - _scrollingState = _CPMenuWindowScrollingStateDown; - - if (_scrollingState != oldScrollingState) - - if (_scrollingState == _CPMenuWindowScrollingStateNone) - [CPEvent stopPeriodicEvents]; - - else if (oldScrollingState == _CPMenuWindowScrollingStateNone) - [CPEvent startPeriodicEventsAfterDelay:0.0 withPeriod:0.04]; - } - else if (type === CPLeftMouseUp && ([anEvent timestamp] - _startTime > STICKY_TIME_INTERVAL)) - [menu cancelTracking]; - } +- (_CPManagerScrollingState)scrollingStateForPoint:(CGPoint)aGlobalLocation +{ + var frame = [self frame]; - // If the item has a submenu, show it. - if ([activeItem hasSubmenu])// && [activeItem action] === @selector(submenuAction:)) - { - var activeItemRect = [activeMenuWindow rectForItemAtIndex:activeItemIndex], - newMenuOrigin = CGPointMake(CGRectGetMaxX(activeItemRect), CGRectGetMinY(activeItemRect)); + if (![self canScroll]) + return _CPMenuManagerScrollingStateNone; - newMenuOrigin = [activeMenuWindow convertBaseToGlobal:newMenuOrigin]; + // If we're at or above of the top scroll indicator... + if (aGlobalLocation.y < CGRectGetMinY(frame) + TOP_MARGIN + SCROLL_INDICATOR_HEIGHT) + return _CPMenuManagerScrollingStateUp; - [self showMenu:[activeItem submenu] fromMenu:[activeItem menu] atPoint:newMenuOrigin]; - } + // If we're at or below the bottom scroll indicator... + if (aGlobalLocation.y > CGRectGetMaxY(frame) - BOTTOM_MARGIN - SCROLL_INDICATOR_HEIGHT) + return _CPMenuManagerScrollingStateDown; - // This handles both the case where we've moved away from the menu, and where - // we've moved to an item without a submenu. - else - [self showMenu:nil fromMenu:activeMenu atPoint:CGPointMakeZero()]; + return _CPMenuManagerScrollingStateNone; +} + +- (CGPoint)rectForItemAtIndex:(int)anIndex +{ + return [_menuView convertRect:[_menuView rectForItemAtIndex:anIndex] toView:nil]; +} + +- (int)itemIndexAtPoint:(CGPoint)aPoint +{ + return [_menuView itemIndexAtPoint:[_menuView convertPoint:aPoint fromView:nil]]; } @end diff --git a/AppKit/CPMenuItem/_CPMenuItemMenuBarView.j b/AppKit/CPMenuItem/_CPMenuItemMenuBarView.j index 4238db868..d860d272b 100644 --- a/AppKit/CPMenuItem/_CPMenuItemMenuBarView.j +++ b/AppKit/CPMenuItem/_CPMenuItemMenuBarView.j @@ -61,14 +61,17 @@ var SUBMENU_INDICATOR_COLOR = nil, [_imageAndTextView setImagePosition:CPImageLeft]; [_imageAndTextView setTextShadowOffset:CGSizeMake(0.0, 1.0)]; + [_imageAndTextView setAutoresizingMask:CPViewMinYMargin | CPViewMaxYMargin]; [self addSubview:_imageAndTextView]; _submenuIndicatorView = [[_CPMenuItemMenuBarSubmenuIndicatorView alloc] initWithFrame:CGRectMake(0.0, 0.0, 9.0, 6.0)]; - [_submenuIndicatorView setAutoresizingMask:CPViewMinXMargin]; + [_submenuIndicatorView setAutoresizingMask:CPViewMinYMargin | CPViewMaxYMargin]; [self addSubview:_submenuIndicatorView]; + + [self setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]; } return self; @@ -155,26 +158,15 @@ var SUBMENU_INDICATOR_COLOR = nil, [self setBackgroundColor:_CPMenuItemSelectionColor]; [_imageAndTextView setTextColor:[CPColor whiteColor]]; - [_keyEquivalentView setTextColor:[CPColor whiteColor]]; - [_imageAndTextView setTextShadowColor:_CPMenuItemTextShadowColor]; - [_keyEquivalentView setTextShadowColor:_CPMenuItemTextShadowColor]; } else { [self setBackgroundColor:nil]; [_imageAndTextView setTextColor:[self textColor]]; - [_keyEquivalentView setTextColor:[self textColor]]; - [_imageAndTextView setTextShadowColor:[self textShadowColor]]; - [_keyEquivalentView setTextShadowColor:[self textShadowColor]]; } - - if (shouldHighlight) - [_stateView setImage:_CPMenuItemDefaultStateHighlightedImages[[_menuItem state]] || nil]; - else - [_stateView setImage:_CPMenuItemDefaultStateImages[[_menuItem state]] || nil]; } @end diff --git a/AppKit/CPMenuItem/_CPMenuItemView.j b/AppKit/CPMenuItem/_CPMenuItemView.j index ee6dd224d..31f7da863 100644 --- a/AppKit/CPMenuItem/_CPMenuItemView.j +++ b/AppKit/CPMenuItem/_CPMenuItemView.j @@ -65,7 +65,7 @@ var _CPMenuItemSelectionColor = nil, - (id)initWithFrame:(CGRect)aFrame forMenuItem:(CPMenuItem)aMenuItem { self = [super initWithFrame:aFrame]; - + if (self) { _menuItem = aMenuItem; @@ -106,7 +106,7 @@ var _CPMenuItemSelectionColor = nil, else if (menuItemView && _view !== menuItemView) { [_view removeFromSuperview]; - _view = view; + _view = menuItemView; } else if ([_menuItem menu] == [CPApp mainMenu])