Merge Headers/AppKit/NSMenu.h Source/NSMenuItemCell.m Source/NSMenuView.m Source/NSPopUpButtonCell.m

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/branches/gnustep_testplant_branch@38732 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Marcian Lytwyn 2015-07-01 23:01:02 +00:00
parent 03f5d6b879
commit a5f529e0df
4 changed files with 280 additions and 165 deletions

View file

@ -45,6 +45,7 @@
@class NSView;
@class NSWindow;
@class NSMutableArray;
@class NSScreen;
/* ******************* */
/* NSMenuView Protocol */
@ -194,6 +195,63 @@
@end
/**
* The NSMenuDelegate protocol defines optional methods implemented
* by delegates of NSMenu objects.
*/
@protocol NSMenuDelegate <NSObject>
/**
* Allows the delegate to return the target and action for a key-down event.
*/
- (BOOL) menuHasKeyEquivalent: (NSMenu *)menu
forEvent: (NSEvent *)event
target: (id *)target
action: (SEL *)action;
/**
* Invoked on the delegate to allow changes before the menu opens.
*/
- (void) menuWillOpen: (NSMenu *)menu;
/**
* Invoked when the menu is about to be displayed.
*/
- (NSInteger) numberOfItemsInMenu: (NSMenu *)menu;
/**
* Invoked to indicate that the menu is about to be updated.
*/
- (void) menuNeedsUpdate: (NSMenu *)menu;
/**
* Invoked to inform the delegate that the menu did close.
*/
- (void) menuDidClose: (NSMenu *)menu;
/**
* Invoked too notify the delegate that the item will be highlighted.
*/
- (void) menu: (NSMenu *)menu
willHighlightItem: (NSMenuItem *)item;
/**
* Invoked to allow the delegate to update an item before
* it is displayed.
*/
- (BOOL) menu: (NSMenu *)menu
updateItem: (NSMenuItem *)item
atIndex: (NSInteger)index
shouldCancel: (BOOL)shouldCancel;
/**
* Specify a display location for the menu
*/
- (NSRect)confinementRectForMenu: (NSMenu *)menu
onScreen: (NSScreen *)screen;
@end
/** <p>Menus provide the user with a list of actions and/or submenus.
* Submenus themselves are full fledged menus and so a heirarchical
* structure of appears.</p>
@ -501,7 +559,7 @@
/** Returns an item located at index.
*/
- (id <NSMenuItem>) itemAtIndex: (NSInteger)index;
- (NSMenuItem *) itemAtIndex: (NSInteger)index;
/** Informs the menu that the specified item has changed.
*/
@ -569,10 +627,6 @@
*/
- (void) removeItemAtIndex: (NSInteger)index;
/** Removes all menu items.
*/
- (void) removeAllItems;
/** Sets if a menu does autoenable.
*/
- (void) setAutoenablesItems: (BOOL)flag;

View file

@ -530,29 +530,12 @@ static NSString *commandKeyString = @"#";
if (_needs_sizing)
[self calcSize];
#if 0
#if 0 //Testplant-MAL-2015-06-20: code moved into NSPopUpButtonCell...
if (_mcell_belongs_to_popupbutton && _cell.image_position)
{
#if 0 //Main branch code...
//Tesplant-MAL-2015-06-22 - should fix branch version below but need
//to fix check mark being displayed on main pop up button rather than
//just on the showing the check mark on the selected item in the
//drop down list...
// Special case: draw image on the extreme right
cellFrame.origin.x += [_menuView imageAndTitleOffset];
cellFrame.size.width -= [_menuView imageAndTitleOffset];
#else //Testplant branch code - MAL-2015-06-20: ensure correct available width returned...
// TODO: Need to find this out somehow...Testplant-MAL
static const NSUInteger ButtonMargin = 5;
CGFloat inset = _imageWidth + ButtonMargin;
// Special case: image is drawn on the extreme right
// First inset the title rect...Testplant-MAL
cellFrame = NSInsetRect(cellFrame, ButtonMargin, 0);
// Adjust for image on right side i.e. down arrow popup indicator...Testplant-MAL
cellFrame.size.width -= _imageWidth;
#endif
return cellFrame;
}
#endif
@ -610,15 +593,11 @@ static NSString *commandKeyString = @"#";
case NSImageLeft:
cellFrame.origin.x += _imageWidth + GSCellTextImageXDist;
// the below line doesn't always clip correctly (eg drop down indicator images)
//cellFrame.size.width = cellFrame.size.width - (_imageWidth + GSCellTextImageXDist);
cellFrame.size.width = _titleWidth;
cellFrame.size.width = cellFrame.size.width - (_imageWidth + GSCellTextImageXDist);
break;
case NSImageRight:
// the below line doesn't always clip correctly (eg drop down indicator images)
//cellFrame.size.width = cellFrame.size.width - (_imageWidth + GSCellTextImageXDist);
cellFrame.size.width = _titleWidth;
cellFrame.size.width = cellFrame.size.width - (_imageWidth + GSCellTextImageXDist);
break;
case NSImageBelow:

View file

@ -209,6 +209,13 @@ static float menuBarHeight = 0.0;
// Create an array to store our menu item cells.
_itemCells = [NSMutableArray new];
// FIXME: Should this go in NSMenu instead of here?
[[NSNotificationCenter defaultCenter]
addObserver: self
selector: @selector(_themeDidActivate:)
name: GSThemeDidActivateNotification
object: nil];
return self;
}
@ -226,6 +233,8 @@ static float menuBarHeight = 0.0;
- (void) dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver: self];
// We must remove the menu view from the menu list of observers.
if (_attachedMenu != nil)
{
@ -360,14 +369,16 @@ static float menuBarHeight = 0.0;
ASSIGN(_font, font);
if (_font != nil)
{
const CGFloat themeHeight = [[GSTheme theme] menuItemHeight];
NSRect r;
r = [_font boundingRectForFont];
/* Should make up 110, 20 for default font */
_cellSize = NSMakeSize (r.size.width * 10., r.size.height + 3.);
if (_cellSize.height < 20)
_cellSize.height = 20;
if (_cellSize.height < themeHeight)
_cellSize.height = themeHeight;
[self setNeedsSizing: YES];
}
@ -433,7 +444,7 @@ static float menuBarHeight = 0.0;
- (NSMenuItemCell*) menuItemCellForItemAtIndex: (NSInteger)index
{
if (index < [_itemCells count])
if ((index >= 0) && (index < [_itemCells count]))
return [_itemCells objectAtIndex: index];
else
return nil;
@ -690,6 +701,50 @@ static float menuBarHeight = 0.0;
return _needsSizing;
}
- (CGFloat) heightForItem: (NSInteger)idx
{
NSMenuItemCell *cell = [self menuItemCellForItemAtIndex: idx];
if (cell != nil)
{
NSMenuItem *item = [cell menuItem];
if ([item isSeparatorItem])
{
return [[GSTheme theme] menuSeparatorHeight];
}
}
return _cellSize.height;
}
- (CGFloat) yOriginForItem: (NSInteger)item
{
const NSInteger count = [_itemCells count];
CGFloat total = 0;
if (item >= 0)
{
NSInteger i = 0;
for (i = (count - 1); i > item; i--)
{
total += [self heightForItem: i];
}
}
return total;
}
- (CGFloat) totalHeight
{
CGFloat total = 0;
NSUInteger i = 0;
for (i = 0; i < [_itemCells count]; i++)
{
total += [self heightForItem: i];
}
return total;
}
- (void) sizeToFit
{
BOOL isPullDown =
@ -717,7 +772,7 @@ static float menuBarHeight = 0.0;
elem.rect = NSMakeRect (currentX,
0,
(2 * _horizontalEdgePad),
_cellSize.height);
[self heightForItem: 0]);
GSIArrayAddItem(cellRects, (GSIArrayItem)elem);
currentX += 2 * _horizontalEdgePad;
}
@ -735,7 +790,7 @@ static float menuBarHeight = 0.0;
elem.rect = NSMakeRect (currentX,
0,
(titleWidth + (2 * _horizontalEdgePad)),
_cellSize.height);
[self heightForItem: i]);
GSIArrayAddItem(cellRects, (GSIArrayItem)elem);
currentX += titleWidth + (2 * _horizontalEdgePad);
@ -903,9 +958,9 @@ static float menuBarHeight = 0.0;
}
[self setFrameSize: NSMakeSize(_cellSize.width + _leftBorderOffset,
(howMany * _cellSize.height)
[self totalHeight]
+ menuBarHeight)];
[_titleView setFrame: NSMakeRect (0, howMany * _cellSize.height,
[_titleView setFrame: NSMakeRect (0, [self totalHeight],
NSWidth (_bounds), menuBarHeight)];
}
_needsSizing = NO;
@ -1003,10 +1058,10 @@ static float menuBarHeight = 0.0;
{
NSRect theRect;
theRect.origin.y
= _cellSize.height * ([_itemCells count] - index - 1);
theRect.origin.y = [self yOriginForItem: index];
theRect.origin.x = _leftBorderOffset;
theRect.size = _cellSize;
theRect.size.height = [self heightForItem: index];
/* NOTE: This returns the correct NSRect for drawing cells, but nothing
* else (unless we are a popup). This rect will have to be modified for
@ -1058,6 +1113,8 @@ static float menuBarHeight = 0.0;
{
NSRect frame = [_window frame];
NSRect submenuFrame;
const CGFloat submenuHorizOverlap = [[GSTheme theme] menuSubmenuHorizontalOverlap];
const CGFloat submenuVertOverlap = [[GSTheme theme] menuSubmenuVerticalOverlap];
if (_needsSizing)
[self sizeToFit];
@ -1077,7 +1134,7 @@ static float menuBarHeight = 0.0;
[_attachedMenu indexOfItemWithSubmenu: aSubmenu]] toView: nil];
NSPoint subOrigin = [_window convertBaseToScreen: aRect.origin];
return NSMakePoint (NSMaxX(frame),
return NSMakePoint (NSMaxX(frame) - submenuHorizOverlap,
subOrigin.y - NSHeight(submenuFrame) - 2 +
2*[NSMenuView menuBarHeight]);
}
@ -1088,12 +1145,12 @@ static float menuBarHeight = 0.0;
NSPoint subOrigin = [_window convertBaseToScreen: aRect.origin];
// FIXME ... why is the offset +1 needed below?
return NSMakePoint (NSMaxX(frame),
return NSMakePoint (NSMaxX(frame) - submenuHorizOverlap,
subOrigin.y - NSHeight(submenuFrame) + aRect.size.height + 1);
}
else
{
return NSMakePoint(NSMaxX(frame),
return NSMakePoint(NSMaxX(frame) - submenuHorizOverlap,
NSMaxY(frame) - NSHeight(submenuFrame));
}
}
@ -1103,9 +1160,19 @@ static float menuBarHeight = 0.0;
[_attachedMenu indexOfItemWithSubmenu: aSubmenu]] toView: nil];
NSPoint subOrigin = [_window convertBaseToScreen: aRect.origin];
/*We add +1 for don't lose the track when the user move the
mouse from the horizontal menu to a submenu.*/
return NSMakePoint(subOrigin.x, subOrigin.y - NSHeight(submenuFrame) + 1);
/* If menu is in window, we add +1 for don't lose the track when
the user move the mouse from the horizontal menu to a submenu.*/
if (NSInterfaceStyleForKey(@"NSMenuInterfaceStyle", nil) ==
NSWindows95InterfaceStyle)
{
return NSMakePoint(subOrigin.x,
subOrigin.y - NSHeight(submenuFrame) + 1 + submenuVertOverlap);
}
else
{
return NSMakePoint(subOrigin.x,
subOrigin.y - NSHeight(submenuFrame) + submenuVertOverlap);
}
}
}
@ -1442,9 +1509,9 @@ static float menuBarHeight = 0.0;
}
if (style == NSWindows95InterfaceStyle)
{
return YES;
}
{
return YES;
}
if (subMenusNeedRemoving)
{
@ -1466,6 +1533,7 @@ static float menuBarHeight = 0.0;
BOOL justAttachedNewSubmenu = NO;
BOOL subMenusNeedRemoving = YES;
BOOL shouldFinish = YES;
BOOL popUpProcessEvents = [[GSTheme theme] doesProcessEventsForPopUpMenu];
int delayCount = 0;
int indexOfActionToExecute = -1;
int firstIndex = -1;
@ -1512,8 +1580,9 @@ static float menuBarHeight = 0.0;
eventMask |= NSLeftMouseDownMask;
/*We need know if the user press a modifier key to close the menu
when the menu is in a window*/
if (style == NSWindows95InterfaceStyle)
when the menu is in a window or when is owned by a popup and theme
process events. */
if (style == NSWindows95InterfaceStyle || popUpProcessEvents)
{
eventMask |= NSFlagsChangedMask;
}
@ -1527,41 +1596,52 @@ static float menuBarHeight = 0.0;
over the menu, the menu is closed when the user releases the mouse. On
the other hand, when the user clicks on the button and then moves the
mouse the menu is closed upon the next mouse click. */
([[self menu] _ownedByPopUp] &&
style == NSMacintoshInterfaceStyle))
([[self menu] _ownedByPopUp] && (style == NSMacintoshInterfaceStyle ||
popUpProcessEvents)))
{
/*
* Ignore the first mouse up if nothing interesting has happened.
*/
shouldFinish = NO;
}
do
{
/* Close the menu if the user press a modifier key and menu
is in a window */
if (mainWindowMenuView != nil && type == NSFlagsChanged)
{
[self setHighlightedItemIndex: -1];
[[[mainWindowMenuView menu] attachedMenu] close];
return NO;
}
if (type == NSFlagsChanged)
{
/* Close the menu if the user press a modifier key and menu
is in a window */
if (mainWindowMenuView != nil)
{
[self setHighlightedItemIndex: -1];
[[[mainWindowMenuView menu] attachedMenu] close];
return NO;
}
/* Close the menu if is owned by a popup and theme process events */
if ([[self menu] _ownedByPopUp] && popUpProcessEvents)
{
[[[self menu] _owningPopUp] dismissPopUp];
return NO;
}
}
if (type == NSLeftMouseUp ||
type == NSRightMouseUp ||
type == NSOtherMouseUp)
type == NSRightMouseUp ||
type == NSOtherMouseUp)
{
shouldFinish = YES;
}
if (type == NSPeriodic || event == original)
{
NSPoint location;
int index;
location = [_window mouseLocationOutsideOfEventStream];
index = [self indexOfItemAtPoint:
[self convertPoint: location fromView: nil]];
index = [self indexOfItemAtPoint:
[self convertPoint: location fromView: nil]];
if (event == original)
{
firstIndex = index;
@ -1570,11 +1650,11 @@ static float menuBarHeight = 0.0;
{
shouldFinish = YES;
}
/*
* 1 - if menus is only partly visible and the mouse is at the
* edge of the screen we move the menu so it will be visible.
*/
*/
if ([_attachedMenu isPartlyOffScreen])
{
NSPoint pointerLoc = [_window convertBaseToScreen: location];
@ -1585,18 +1665,18 @@ static float menuBarHeight = 0.0;
* GNUstep screen coordinates start with 1.
*/
if (pointerLoc.x == 0 || pointerLoc.y == 1
|| pointerLoc.x == screenFrame.size.width - 1
|| pointerLoc.y == screenFrame.size.height)
|| pointerLoc.x == screenFrame.size.width - 1
|| pointerLoc.y == screenFrame.size.height)
[_attachedMenu shiftOnScreen];
}
/*
* 2 - Check if we have to reset the justAttachedNewSubmenu
* flag to NO.
*/
if (justAttachedNewSubmenu && index != -1
&& index != _highlightedItemIndex)
{
&& index != _highlightedItemIndex)
{
if (location.x - lastLocation.x > MOVE_THRESHOLD_DELTA)
{
delayCount ++;
@ -1610,20 +1690,20 @@ static float menuBarHeight = 0.0;
justAttachedNewSubmenu = NO;
}
}
// 3 - If we have moved outside this menu, take appropriate action
if (index == -1)
{
NSPoint locationInScreenCoordinates;
NSWindow *windowUnderMouse;
NSMenu *candidateMenu;
subMenusNeedRemoving = NO;
locationInScreenCoordinates
= [_window convertBaseToScreen: location];
= [_window convertBaseToScreen: location];
/*
* 3a - Check if moved into one of the ancestor menus.
* This is tricky, there are a few possibilities:
@ -1633,124 +1713,122 @@ static float menuBarHeight = 0.0;
* We are a root: isTornOff of AppMenu
*/
candidateMenu = [_attachedMenu supermenu];
while (candidateMenu
&& !NSMouseInRect (locationInScreenCoordinates,
[[candidateMenu window] frame], NO) // not found yet
&& (! ([candidateMenu isTornOff]
&& ![candidateMenu isTransient])) // no root of display tree
&& [candidateMenu isAttached]) // has displayed parent
while (candidateMenu
&& !NSMouseInRect (locationInScreenCoordinates,
[[candidateMenu window] frame], NO) // not found yet
&& (! ([candidateMenu isTornOff]
&& ![candidateMenu isTransient])) // no root of display tree
&& [candidateMenu isAttached]) // has displayed parent
{
candidateMenu = [candidateMenu supermenu];
}
if (candidateMenu != nil
&& NSMouseInRect (locationInScreenCoordinates,
[[candidateMenu window] frame], NO))
&& NSMouseInRect (locationInScreenCoordinates,
[[candidateMenu window] frame], NO))
{
BOOL candidateMenuResult;
NSMenuView *subMenuView = [[candidateMenu attachedMenu] menuRepresentation];
// The call to fetch attachedMenu is not needed. But putting
// it here avoids flicker when we go back to an ancestor
// it here avoids flicker when we go back to an ancestor
// menu and the attached menu is already correct.
[subMenuView detachSubmenu];
// Reset highlighted index for this menu.
// This way if we return to this submenu later there
// This way if we return to this submenu later there
// won't be a highlighted item.
[subMenuView setHighlightedItemIndex: -1];
candidateMenuResult = [[candidateMenu menuRepresentation]
_trackWithEvent: original
startingMenuView: mainWindowMenuView];
_trackWithEvent: original
startingMenuView: mainWindowMenuView];
return candidateMenuResult;
}
// 3b - Check if we enter the attached submenu
windowUnderMouse = [[_attachedMenu attachedMenu] window];
if (windowUnderMouse != nil
&& NSMouseInRect (locationInScreenCoordinates,
[windowUnderMouse frame], NO))
&& NSMouseInRect (locationInScreenCoordinates,
[windowUnderMouse frame], NO))
{
BOOL wasTransient = [_attachedMenu isTransient];
BOOL subMenuResult;
subMenuResult
= [[self attachedMenuView] _trackWithEvent: original
startingMenuView: mainWindowMenuView];
subMenuResult = [[self attachedMenuView] _trackWithEvent: original
startingMenuView: mainWindowMenuView];
if (subMenuResult
&& wasTransient == [_attachedMenu isTransient])
&& wasTransient == [_attachedMenu isTransient])
{
[self detachSubmenu];
}
return subMenuResult;
}
/* We track the menu correctly when this is located
in a window */
if (mainWindowMenuView != nil)
{
/* We track the menu correctly when this is located
in a window */
if (mainWindowMenuView != nil)
{
// If the user moves the mouse into the main window
// horizontal menu, start tracking again.
NSWindow *mainWindow = [mainWindowMenuView window];
NSPoint locationInMainWindow = [mainWindow
convertScreenToBase: locationInScreenCoordinates];
if ([mainWindowMenuView
hitTest: locationInMainWindow] != nil)
{
int index = [mainWindowMenuView indexOfItemAtPoint:
[mainWindowMenuView
convertPoint: locationInMainWindow
fromView: nil]];
NSPoint locationInMainWindow = [mainWindow
convertScreenToBase: locationInScreenCoordinates];
if ([mainWindowMenuView
hitTest: locationInMainWindow] != nil)
{
int index = [mainWindowMenuView indexOfItemAtPoint:
[mainWindowMenuView
convertPoint: locationInMainWindow
fromView: nil]];
if (index != -1 &&
index != [mainWindowMenuView highlightedItemIndex])
{
[self setHighlightedItemIndex: -1];
return [mainWindowMenuView
_trackWithEvent: original
startingMenuView: mainWindowMenuView];
[self setHighlightedItemIndex: -1];
return [mainWindowMenuView
_trackWithEvent: original
startingMenuView: mainWindowMenuView];
}
}
}
}
// 4 - We changed the selected item and should update.
if (!justAttachedNewSubmenu && index != _highlightedItemIndex)
{
subMenusNeedRemoving = NO;
[self detachSubmenu];
[self setHighlightedItemIndex: index];
// WO: Question? Why the ivar _items_link
if (index >= 0 && [[_items_link objectAtIndex: index] submenu])
{
[self attachSubmenuForItemAtIndex: index];
justAttachedNewSubmenu = YES;
delayCount = 0;
}
}
}
// 4 - We changed the selected item and should update.
if (!justAttachedNewSubmenu && index != _highlightedItemIndex)
{
subMenusNeedRemoving = NO;
[self detachSubmenu];
[self setHighlightedItemIndex: index];
// WO: Question? Why the ivar _items_link
if (index >= 0 && [[_items_link objectAtIndex: index] submenu])
{
[self attachSubmenuForItemAtIndex: index];
justAttachedNewSubmenu = YES;
delayCount = 0;
}
}
// Update last seen location for the justAttachedNewSubmenu logic.
lastLocation = location;
}
do
{
event = [NSApp nextEventMatchingMask: eventMask
untilDate: theDistantFuture
inMode: NSEventTrackingRunLoopMode
dequeue: YES];
type = [event type];
if (type == NSAppKitDefined)
{
[[event window] sendEvent: event];
}
}
{
event = [NSApp nextEventMatchingMask: eventMask
untilDate: theDistantFuture
inMode: NSEventTrackingRunLoopMode
dequeue: YES];
type = [event type];
if (type == NSAppKitDefined)
{
[[event window] sendEvent: event];
}
}
while (type == NSAppKitDefined);
}
while ((type != NSLeftMouseUp &&
type != NSRightMouseUp &&
type != NSOtherMouseUp) || shouldFinish == NO);
} while ((type != NSLeftMouseUp &&
type != NSRightMouseUp &&
type != NSOtherMouseUp) || shouldFinish == NO);
/*
* Ok, we released the mouse
@ -1941,6 +2019,12 @@ static float menuBarHeight = 0.0;
return [_attachedMenu performKeyEquivalent: theEvent];
}
- (void) _themeDidActivate: (NSNotification*)notification
{
// The new theme may have different menu item sizes,
// so the window size for the menu needs to be recalculated.
[[self menu] sizeToFit];
}
/*
* NSCoding Protocol

View file

@ -328,9 +328,9 @@ static NSImage *_pbc_image[5];
*/
- (void) setAltersStateOfSelectedItem: (BOOL)flag
{
#if 0
id <NSMenuItem> selectedItem = [self selectedItem];
#if 0
if (flag)
{
[selectedItem setState: NSOnState];
@ -909,8 +909,6 @@ static NSImage *_pbc_image[5];
// TODO: Need to find this out somehow...Testplant-MAL
static const NSUInteger ButtonMargin = 5;
CGFloat inset = _imageWidth + ButtonMargin;
// Special case: image is drawn on the extreme right
// First inset the title rect...Testplant-MAL
cellFrame = NSInsetRect(cellFrame, ButtonMargin, 0);
@ -1281,9 +1279,9 @@ static NSImage *_pbc_image[5];
}
if ([aDecoder containsValueForKey: @"NSPullDown"])
{
BOOL pullDown = [aDecoder decodeBoolForKey: @"NSPullDown"];
[self setPullsDown: pullDown];
}
BOOL pullDown = [aDecoder decodeBoolForKey: @"NSPullDown"];
[self setPullsDown: pullDown];
}
if ([aDecoder containsValueForKey: @"NSUsesItemFromMenu"])
{
BOOL usesItem = [aDecoder decodeBoolForKey: @"NSUsesItemFromMenu"];
@ -1305,13 +1303,13 @@ static NSImage *_pbc_image[5];
}
if ([aDecoder containsValueForKey: @"NSSelectedIndex"])
{
int selectedIdx = [aDecoder decodeIntForKey:
@"NSSelectedIndex"];
[self selectItemAtIndex: selectedIdx];
}
int selectedIdx = [aDecoder decodeIntForKey:
@"NSSelectedIndex"];
[self selectItemAtIndex: (selectedIdx < 0) ? 0 : selectedIdx];
}
else
{
[self selectItemAtIndex: 0];
[self selectItemAtIndex: 0];
}
if ([aDecoder containsValueForKey: @"NSMenuItem"])
{