Merge branch 'master' of github.com:gnustep/libs-gui into NSPopover_issue168

This commit is contained in:
Gregory John Casamento 2023-11-11 05:41:18 -05:00
commit 397dda46b3
12 changed files with 377 additions and 240 deletions

View file

@ -235,6 +235,8 @@
#import <AppKit/NSTabView.h>
#import <AppKit/NSPrintPanel.h>
#import <AppKit/NSPageLayout.h>
// For window decorator protocol
#import <GNUstepGUI/GSWindowDecorationView.h>
#if OS_API_VERSION(GS_API_NONE,GS_API_NONE)
@class NSArray;
@ -1244,6 +1246,13 @@ APPKIT_EXPORT_CLASS
state: (int)inputState
andTitle: (NSString*)title;
- (void) setFrameForCloseButton: (NSButton *)closeButton
viewSize: (NSSize)viewSize;
- (NSRect) miniaturizeButtonFrameForBounds: (NSRect)bounds;
- (NSRect) closeButtonFrameForBounds: (NSRect)bounds;
- (NSColor *) browserHeaderTextColor;
- (void) drawBrowserHeaderCell: (NSTableHeaderCell*)cell
@ -1501,6 +1510,44 @@ withRepeatedImage: (NSImage*)image
*/
- (void) updateMenu: (NSMenu *)menu forWindow: (NSWindow *)window;
- (void) updateAllWindowsWithMenu: (NSMenu *) menu;
/**
* Modifies the given NSRect for use by NSMenu to position and size
* the displayed menu. The default implementation simply returns
* the original NSRect unmodified.
*/
- (NSRect) modifyRect: (NSRect)aRect
forMenu: (NSMenu *)aMenu
isHorizontal: (BOOL) horizontal;
/**
* Modifies the proposed default width for a menu title in the given NSMenuView.
* The default implementation simply returns the proposed width unmodified.
*/
- (CGFloat) proposedTitleWidth: (CGFloat)proposedWidth
forMenuView: (NSMenuView *)aMenuView;
/**
* Modifies the proposed key equivalent string for the menu item. The default
* implementation simply returns the proposed string unmodified.
*/
- (NSString *) keyForKeyEquivalent: (NSString *)aString;
/**
* Modifies the proposed menu item title. The default implementation simply
* returns the proposed string unmodified.
*/
- (NSString *) proposedTitle: (NSString *)title
forMenuItem: (NSMenuItem *)menuItem;
/**
* Used by the theme to organize the main menu. The default implementation
* organizes the main menu in the same way that NSMenu's old default behaviour
* did, generating an "app name" menu for horizontal display.
*/
- (void) organizeMenu: (NSMenu *)menu
isHorizontal: (BOOL)horizontal;
@end
@interface GSTheme (OpenSavePanels)
@ -1541,6 +1588,13 @@ APPKIT_EXPORT_CLASS
@end
@interface GSTheme (NSWindow)
/**
* This method returns the window decorator provided by
* the current theme.
*/
- (id<GSWindowDecorator>) windowDecorator;
/**
* This method returns the standard window button for the
* given mask for the current theme.
@ -1594,6 +1648,5 @@ APPKIT_EXPORT_CLASS
- (NSImage *) highlightedBranchImage;
@end
#endif /* OS_API_VERSION */
#endif /* _GNUstep_H_GSTheme */

View file

@ -67,6 +67,7 @@ APPKIT_EXPORT_CLASS
BOOL hasMenu;
BOOL hasToolbar;
}
+ (id<GSWindowDecorator>) windowDecorator;
- (id) initWithFrame: (NSRect)frame window: (NSWindow *)w;

View file

@ -108,11 +108,8 @@
}
if (hasCloseButton)
{
closeButtonRect = NSMakeRect([self bounds].size.width - [theme titlebarButtonSize] -
[theme titlebarPaddingRight], [self bounds].size.height -
[theme titlebarButtonSize] - [theme titlebarPaddingTop],
[theme titlebarButtonSize], [theme titlebarButtonSize]);
[closeButton setFrame: closeButtonRect];
NSRect closeButtonFrame = [[GSTheme theme] closeButtonFrameForBounds: [self bounds]];
[closeButton setFrame: closeButtonFrame];
}
else
{
@ -121,10 +118,8 @@
if (hasMiniaturizeButton)
{
miniaturizeButtonRect = NSMakeRect([theme titlebarPaddingLeft], [self bounds].size.height -
[theme titlebarButtonSize] - [theme titlebarPaddingTop],
[theme titlebarButtonSize], [theme titlebarButtonSize]);
[miniaturizeButton setFrame: miniaturizeButtonRect];
NSRect miniaturizeButtonFrame = [[GSTheme theme] miniaturizeButtonFrameForBounds: [self bounds]];
[miniaturizeButton setFrame: miniaturizeButtonFrame];
}
else
{

View file

@ -967,13 +967,12 @@
// fill oval with background color
[backgroundColor set];
[oval fill];
// and stroke rounded button
[[NSColor shadowColor] set];
[oval stroke];
}
- (void) drawSwitchInRect: (NSRect)rect
forState: (NSControlStateValue)state
enabled: (BOOL)enabled
@ -2073,6 +2072,38 @@ static NSDictionary *titleTextAttributes[3] = {nil, nil, nil};
}
}
- (void) setFrameForCloseButton: (NSButton *)closeButton
viewSize: (NSSize)viewSize
{
NSSize buttonSize = [[closeButton image] size];
buttonSize = NSMakeSize(buttonSize.width + 3, buttonSize.height + 3);
[closeButton setFrame: NSMakeRect(viewSize.width - buttonSize.width - 4,
(viewSize.height - buttonSize.height) / 2,
buttonSize.width,
buttonSize.height)];
}
- (NSRect) closeButtonFrameForBounds: (NSRect)bounds
{
GSTheme *theme = [GSTheme theme];
return NSMakeRect(bounds.size.width - [theme titlebarButtonSize] -
[theme titlebarPaddingRight], bounds.size.height -
[theme titlebarButtonSize] - [theme titlebarPaddingTop],
[theme titlebarButtonSize], [theme titlebarButtonSize]);
}
- (NSRect) miniaturizeButtonFrameForBounds: (NSRect)bounds
{
GSTheme *theme = [GSTheme theme];
return NSMakeRect([theme titlebarPaddingLeft],
bounds.size.height - [theme titlebarButtonSize] - [theme titlebarPaddingTop],
[theme titlebarButtonSize],
[theme titlebarButtonSize]);
}
- (NSColor *) browserHeaderTextColor
{
NSColor *color;

View file

@ -33,6 +33,7 @@
#import "AppKit/NSWindow.h"
#import "AppKit/NSMenuView.h"
#import "AppKit/NSApplication.h"
#import "AppKit/NSImage.h"
#import "GNUstepGUI/GSTheme.h"
#import "GNUstepGUI/GSWindowDecorationView.h"
@ -58,6 +59,14 @@
}
@end
@interface NSMenu (GNUstepPrivate)
- (BOOL) _isMain;
- (void) _organizeMenu;
@end
@implementation GSTheme (Menu)
- (void) setMenu: (NSMenu *)menu
forWindow: (NSWindow *)window
@ -175,5 +184,239 @@
return YES; // override whether or not to show the icon in the menu.
}
- (NSRect) modifyRect: (NSRect)aRect
forMenu: (NSMenu *)aMenu
isHorizontal: (BOOL) horizontal;
{
return aRect;
}
- (CGFloat) proposedTitleWidth: (CGFloat)proposedWidth
forMenuView: (NSMenuView *)aMenuView
{
return proposedWidth;
}
- (NSString *) keyForKeyEquivalent: (NSString *)aString
{
return aString;
}
- (NSString *) proposedTitle: (NSString *)title
forMenuItem: (NSMenuItem *)menuItem
{
return title;
}
- (void) organizeMenu: (NSMenu *)menu
isHorizontal: (BOOL)horizontal
{
NSString *infoString = _(@"Info");
NSString *servicesString = _(@"Services");
int i;
if ([menu _isMain])
{
NSString *appTitle;
NSMenu *appMenu;
id <NSMenuItem> appItem;
appTitle = [[[NSBundle mainBundle] localizedInfoDictionary]
objectForKey: @"ApplicationName"];
if (nil == appTitle)
{
appTitle = [[NSProcessInfo processInfo] processName];
}
appItem = [menu itemWithTitle: appTitle];
appMenu = [appItem submenu];
if (horizontal == YES)
{
NSMutableArray *itemsToMove;
itemsToMove = [NSMutableArray new];
if (appMenu == nil)
{
[menu insertItemWithTitle: appTitle
action: NULL
keyEquivalent: @""
atIndex: 0];
appItem = [menu itemAtIndex: 0];
appMenu = [NSMenu new];
[menu setSubmenu: appMenu forItem: appItem];
RELEASE(appMenu);
}
else
{
int index = [menu indexOfItem: appItem];
if (index != 0)
{
RETAIN (appItem);
[menu removeItemAtIndex: index];
[menu insertItem: appItem atIndex: 0];
RELEASE (appItem);
}
}
if ([[GSTheme theme] menuShouldShowIcon])
{
NSImage *ti;
float bar;
ti = [[NSApp applicationIconImage] copy];
if (ti == nil)
{
ti = [[NSImage imageNamed: @"GNUstep"] copy];
}
[ti setScalesWhenResized: YES];
bar = [NSMenuView menuBarHeight] - 4;
[ti setSize: NSMakeSize(bar, bar)];
[appItem setImage: ti];
RELEASE(ti);
}
// Collect all simple items plus "Info" and "Services"
for (i = 1; i < [[menu itemArray] count]; i++)
{
NSMenuItem *anItem = [[menu itemArray] objectAtIndex: i];
NSString *title = [anItem title];
NSMenu *submenu = [anItem submenu];
if (submenu == nil)
{
[itemsToMove addObject: anItem];
}
else
{
// The menu may not be localized, so we have to
// check both the English and the local version.
if ([title isEqual: @"Info"] ||
[title isEqual: @"Services"] ||
[title isEqual: infoString] ||
[title isEqual: servicesString])
{
[itemsToMove addObject: anItem];
}
}
}
for (i = 0; i < [itemsToMove count]; i++)
{
NSMenuItem *anItem = [itemsToMove objectAtIndex: i];
[menu removeItem: anItem];
[appMenu addItem: anItem];
}
RELEASE(itemsToMove);
}
else
{
[appItem setImage: nil];
if (appMenu != nil)
{
NSArray *array = [NSArray arrayWithArray: [appMenu itemArray]];
/*
* Everything above the Serives menu goes into the info submenu,
* the rest into the main menu.
*/
int k = [appMenu indexOfItemWithTitle: servicesString];
// The menu may not be localized, so we have to
// check both the English and the local version.
if (k == -1)
k = [appMenu indexOfItemWithTitle: @"Services"];
if ((k > 0) && ([[array objectAtIndex: k - 1] isSeparatorItem]))
k--;
if (k == 1)
{
// Exactly one info item
NSMenuItem *anItem = [array objectAtIndex: 0];
[appMenu removeItem: anItem];
[menu insertItem: anItem atIndex: 0];
}
else if (k > 1)
{
id <NSMenuItem> infoItem;
NSMenu *infoMenu;
// Multiple info items, add a submenu for them
[menu insertItemWithTitle: infoString
action: NULL
keyEquivalent: @""
atIndex: 0];
infoItem = [menu itemAtIndex: 0];
infoMenu = [NSMenu new];
[menu setSubmenu: infoMenu forItem: infoItem];
RELEASE(infoMenu);
for (i = 0; i < k; i++)
{
NSMenuItem *anItem = [array objectAtIndex: i];
[appMenu removeItem: anItem];
[infoMenu addItem: anItem];
}
}
else
{
// No service menu, or it is the first item.
// We still look for an info item.
NSMenuItem *anItem = [array objectAtIndex: 0];
NSString *title = [anItem title];
// The menu may not be localized, so we have to
// check both the English and the local version.
if ([title isEqual: @"Info"] ||
[title isEqual: infoString])
{
[appMenu removeItem: anItem];
[menu insertItem: anItem atIndex: 0];
k = 1;
}
else
{
k = 0;
}
}
// Copy the remaining entries.
for (i = k; i < [array count]; i++)
{
NSMenuItem *anItem = [array objectAtIndex: i];
[appMenu removeItem: anItem];
[menu addItem: anItem];
}
[menu removeItem: appItem];
}
}
}
// recurse over all submenus
for (i = 0; i < [[menu itemArray] count]; i++)
{
NSMenuItem *anItem = [[menu itemArray] objectAtIndex: i];
NSMenu *submenu = [anItem submenu];
if (submenu != nil)
{
if ([submenu isTransient])
{
[submenu closeTransient];
}
[submenu close];
[submenu _organizeMenu];
}
}
[[menu menuRepresentation] update];
[menu sizeToFit];
}
@end

View file

@ -33,6 +33,7 @@
#import "GNUstepGUI/GSTheme.h"
#import "GNUstepGUI/GSWindowDecorationView.h"
#import "GSThemePrivate.h"
#import "GNUstepGUI/GSDisplayServer.h"
@implementation GSTheme (NSWindow)
- (NSButton *) standardWindowButton: (NSWindowButton)button
@ -86,4 +87,13 @@
{
// default implementation does nothing...
}
- (id<GSWindowDecorator>) windowDecorator
{
if ([GSCurrentServer() handlesWindowDecorations])
return [GSBackendWindowDecorationView self];
else
return [GSStandardWindowDecorationView self];
}
@end

View file

@ -440,15 +440,9 @@
[closeButton setTarget: _owner];
[closeButton setAction: closeAction];
viewSize = [self frame].size;
buttonSize = [[closeButton image] size];
buttonSize = NSMakeSize(buttonSize.width + 3, buttonSize.height + 3);
// Update location
[closeButton setFrame:
NSMakeRect(viewSize.width - buttonSize.width - 4,
(viewSize.height - buttonSize.height) / 2,
buttonSize.width, buttonSize.height)];
[[GSTheme theme] setFrameForCloseButton: closeButton
viewSize: [self frame].size];
[closeButton setAutoresizingMask: NSViewMinXMargin | NSViewMaxYMargin];
}

View file

@ -64,10 +64,7 @@ static inline NSRect RectWithSizeScaledByFactor(NSRect aRect, CGFloat factor)
+ (id<GSWindowDecorator>) windowDecorator
{
if ([GSCurrentServer() handlesWindowDecorations])
return [GSBackendWindowDecorationView self];
else
return [GSStandardWindowDecorationView self];
return [[GSTheme theme] windowDecorator];
}

View file

@ -261,226 +261,28 @@ static BOOL menuBarVisible = YES;
- (void) _organizeMenu
{
NSString *infoString = _(@"Info");
NSString *servicesString = _(@"Services");
int i;
if ([self _isMain])
{
NSString *appTitle;
NSMenu *appMenu;
id <NSMenuItem> appItem;
appTitle = [[[NSBundle mainBundle] localizedInfoDictionary]
objectForKey: @"ApplicationName"];
if (nil == appTitle)
{
appTitle = [[NSProcessInfo processInfo] processName];
}
appItem = [self itemWithTitle: appTitle];
appMenu = [appItem submenu];
if (_menu.horizontal == YES)
{
NSMutableArray *itemsToMove;
itemsToMove = [NSMutableArray new];
if (appMenu == nil)
{
[self insertItemWithTitle: appTitle
action: NULL
keyEquivalent: @""
atIndex: 0];
appItem = [self itemAtIndex: 0];
appMenu = [NSMenu new];
[self setSubmenu: appMenu forItem: appItem];
RELEASE(appMenu);
}
else
{
int index = [self indexOfItem: appItem];
if (index != 0)
{
RETAIN (appItem);
[self removeItemAtIndex: index];
[self insertItem: appItem atIndex: 0];
RELEASE (appItem);
}
}
if ([[GSTheme theme] menuShouldShowIcon])
{
NSImage *ti;
float bar;
ti = [[NSApp applicationIconImage] copy];
if (ti == nil)
{
ti = [[NSImage imageNamed: @"GNUstep"] copy];
}
[ti setScalesWhenResized: YES];
bar = [NSMenuView menuBarHeight] - 4;
[ti setSize: NSMakeSize(bar, bar)];
[appItem setImage: ti];
RELEASE(ti);
}
// Collect all simple items plus "Info" and "Services"
for (i = 1; i < [_items count]; i++)
{
NSMenuItem *anItem = [_items objectAtIndex: i];
NSString *title = [anItem title];
NSMenu *submenu = [anItem submenu];
if (submenu == nil)
{
[itemsToMove addObject: anItem];
}
else
{
// The menu may not be localized, so we have to
// check both the English and the local version.
if ([title isEqual: @"Info"] ||
[title isEqual: @"Services"] ||
[title isEqual: infoString] ||
[title isEqual: servicesString])
{
[itemsToMove addObject: anItem];
}
}
}
for (i = 0; i < [itemsToMove count]; i++)
{
NSMenuItem *anItem = [itemsToMove objectAtIndex: i];
[self removeItem: anItem];
[appMenu addItem: anItem];
}
RELEASE(itemsToMove);
}
else
{
[appItem setImage: nil];
if (appMenu != nil)
{
NSArray *array = [NSArray arrayWithArray: [appMenu itemArray]];
/*
* Everything above the Serives menu goes into the info submenu,
* the rest into the main menu.
*/
int k = [appMenu indexOfItemWithTitle: servicesString];
// The menu may not be localized, so we have to
// check both the English and the local version.
if (k == -1)
k = [appMenu indexOfItemWithTitle: @"Services"];
if ((k > 0) && ([[array objectAtIndex: k - 1] isSeparatorItem]))
k--;
if (k == 1)
{
// Exactly one info item
NSMenuItem *anItem = [array objectAtIndex: 0];
[appMenu removeItem: anItem];
[self insertItem: anItem atIndex: 0];
}
else if (k > 1)
{
id <NSMenuItem> infoItem;
NSMenu *infoMenu;
// Multiple info items, add a submenu for them
[self insertItemWithTitle: infoString
action: NULL
keyEquivalent: @""
atIndex: 0];
infoItem = [self itemAtIndex: 0];
infoMenu = [NSMenu new];
[self setSubmenu: infoMenu forItem: infoItem];
RELEASE(infoMenu);
for (i = 0; i < k; i++)
{
NSMenuItem *anItem = [array objectAtIndex: i];
[appMenu removeItem: anItem];
[infoMenu addItem: anItem];
}
}
else
{
// No service menu, or it is the first item.
// We still look for an info item.
NSMenuItem *anItem = [array objectAtIndex: 0];
NSString *title = [anItem title];
// The menu may not be localized, so we have to
// check both the English and the local version.
if ([title isEqual: @"Info"] ||
[title isEqual: infoString])
{
[appMenu removeItem: anItem];
[self insertItem: anItem atIndex: 0];
k = 1;
}
else
{
k = 0;
}
}
// Copy the remaining entries.
for (i = k; i < [array count]; i++)
{
NSMenuItem *anItem = [array objectAtIndex: i];
[appMenu removeItem: anItem];
[self addItem: anItem];
}
[self removeItem: appItem];
}
}
}
// recurse over all submenus
for (i = 0; i < [_items count]; i++)
{
NSMenuItem *anItem = [_items objectAtIndex: i];
NSMenu *submenu = [anItem submenu];
if (submenu != nil)
{
if ([submenu isTransient])
{
[submenu closeTransient];
}
[submenu close];
[submenu _organizeMenu];
}
}
[[self menuRepresentation] update];
[self sizeToFit];
[[GSTheme theme] organizeMenu: self
isHorizontal: _menu.horizontal];
}
- (void) _setGeometry
{
NSPoint origin;
if (_menu.horizontal == YES)
{
NSRect screenFrame = [[NSScreen mainScreen] frame];
origin = NSMakePoint (0, screenFrame.size.height
- [_aWindow frame].size.height);
origin.y += screenFrame.origin.y;
[_aWindow setFrameOrigin: origin];
[_bWindow setFrameOrigin: origin];
NSRect proposedFrame = NSMakeRect(0,
screenFrame.size.height -
[_aWindow frame].size.height +
screenFrame.origin.y,
screenFrame.size.width,
[_aWindow frame].size.height);
proposedFrame = [[GSTheme theme] modifyRect: proposedFrame
forMenu: self
isHorizontal: YES];
[_aWindow setFrame: proposedFrame display: NO];
[_bWindow setFrame: proposedFrame display: NO];
}
else
{
@ -510,7 +312,7 @@ static BOOL menuBarVisible = YES;
if ((_aWindow != nil) && ([_aWindow screen] != nil))
{
origin = NSMakePoint(0, [[_aWindow screen] visibleFrame].size.height
NSPoint origin = NSMakePoint(0, [[_aWindow screen] visibleFrame].size.height
- [_aWindow frame].size.height);
[_aWindow setFrameOrigin: origin];

View file

@ -42,6 +42,7 @@
#import "AppKit/NSMenuItem.h"
#import "AppKit/NSMenu.h"
#import "GSBindingHelpers.h"
#import "GNUstepGUI/GSTheme.h"
static BOOL usesUserKeyEquivalents = NO;
static Class imageClass;
@ -264,7 +265,12 @@ static Class imageClass;
- (NSString*) title
{
return _title;
NSString *proposedTitle = _title;
proposedTitle = [[GSTheme theme] proposedTitle: proposedTitle
forMenuItem: self];
return proposedTitle;
}
- (BOOL) isSeparatorItem

View file

@ -290,6 +290,8 @@ static NSString *commandKeyString = @"#";
(shift != NO) ? shiftKeyString : @"",
(m & NSCommandKeyMask) ? commandKeyString : @"",
key];
key = [[GSTheme theme] keyForKeyEquivalent: key];
}
}

View file

@ -789,7 +789,10 @@ static float menuBarHeight = 0.0;
{
GSCellRect elem;
NSMenuItemCell *aCell = [self menuItemCellForItemAtIndex: i];
float titleWidth = [aCell titleWidth];
CGFloat titleWidth = [aCell titleWidth];
titleWidth = [[GSTheme theme] proposedTitleWidth: titleWidth
forMenuView: self];
if ([aCell imageWidth])
{