mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 04:57:03 +00:00
Preliminary menu bar support.
Reviewed by me.
This commit is contained in:
+33
-9
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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])
|
||||
|
||||
Reference in New Issue
Block a user