Preliminary menu bar support.

Reviewed by me.
This commit is contained in:
Francisco Ryan Tolmasky I
2009-12-02 15:57:52 -08:00
parent ef12ec54d7
commit 78795e3167
6 changed files with 439 additions and 417 deletions
+33 -9
View File
@@ -25,6 +25,7 @@
@import <Foundation/CPNotificationCenter.j>
@import <Foundation/CPString.j>
@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
+54 -143
View File
@@ -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
+299
View File
@@ -0,0 +1,299 @@
#include "../CoreGraphics/CGGeometry.h"
@import <Foundation/CPObject.j>
_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
+47 -251
View File
@@ -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
+4 -12
View File
@@ -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
+2 -2
View File
@@ -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])