Implement Application Menu for OS X (close #11)

This commit is contained in:
Marco Antognini 2013-09-21 14:01:51 +02:00
parent 369b7faa1c
commit a6dba586ee
9 changed files with 351 additions and 10 deletions

View File

@ -80,6 +80,8 @@ else() # MACOSX
${SRCROOT}/OSX/JoystickImpl.hpp ${SRCROOT}/OSX/JoystickImpl.hpp
${SRCROOT}/OSX/SFApplication.h ${SRCROOT}/OSX/SFApplication.h
${SRCROOT}/OSX/SFApplication.m ${SRCROOT}/OSX/SFApplication.m
${SRCROOT}/OSX/SFApplicationDelegate.h
${SRCROOT}/OSX/SFApplicationDelegate.m
${SRCROOT}/OSX/SFContext.hpp ${SRCROOT}/OSX/SFContext.hpp
${SRCROOT}/OSX/SFContext.mm ${SRCROOT}/OSX/SFContext.mm
${SRCROOT}/OSX/SFKeyboardModifiersHelper.h ${SRCROOT}/OSX/SFKeyboardModifiersHelper.h

View File

@ -88,7 +88,7 @@ public :
/// \brief Drain the pool /// \brief Drain the pool
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void Drain(); void drain();
private: private:
@ -155,7 +155,7 @@ void PoolWrapper::release()
// Drain pool if required. // Drain pool if required.
if (m_count == 0) { if (m_count == 0) {
Drain(); drain();
} }
#ifdef SFML_DEBUG #ifdef SFML_DEBUG
@ -165,7 +165,7 @@ void PoolWrapper::release()
#endif #endif
} }
void PoolWrapper::Drain() void PoolWrapper::drain()
{ {
[m_pool drain]; [m_pool drain];
m_pool = 0; m_pool = 0;
@ -227,7 +227,7 @@ void releasePool(void)
void drainPool() void drainPool()
{ {
if (localPool != NULL) { if (localPool != NULL) {
localPool->Drain(); localPool->drain();
} }
#ifdef SFML_DEBUG #ifdef SFML_DEBUG
else { else {

View File

@ -30,7 +30,7 @@
#import <AppKit/AppKit.h> #import <AppKit/AppKit.h>
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Event processing /// \brief Event processing & Menu bar initialisation
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
@interface SFApplication : NSApplication @interface SFApplication : NSApplication
@ -43,6 +43,13 @@
+(void)processEvent; +(void)processEvent;
////////////////////////////////////////////////////////////
/// \brief Set up the menu bar and its items
///
////////////////////////////////////////////////////////////
+(void)setUpMenuBar;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Dispatch events /// \brief Dispatch events
/// ///

View File

@ -48,6 +48,203 @@
} }
} }
////////////////////////////////////////////////////////
+(void)setUpMenuBar
{
[SFApplication sharedApplication]; // Make sure NSApp exists
// Set the main menu bar
NSMenu* mainMenu = [NSApp mainMenu];
if (mainMenu != nil) return;
mainMenu = [[NSMenu alloc] initWithTitle:@""];
[NSApp setMainMenu:mainMenu];
// Application Menu (aka Apple Menu)
NSMenuItem* appleItem = [mainMenu addItemWithTitle:@"" action:nil keyEquivalent:@""];
NSMenu* appleMenu = [[SFApplication createAppleMenu] autorelease];
[appleItem setSubmenu:appleMenu];
// File Menu
NSMenuItem* fileItem = [mainMenu addItemWithTitle:@"" action:nil keyEquivalent:@""];
NSMenu* fileMenu = [[SFApplication createFileMenu] autorelease];
[fileItem setSubmenu:fileMenu];
// Window menu
NSMenuItem* windowItem = [mainMenu addItemWithTitle:@"" action:nil keyEquivalent:@""];
NSMenu* windowMenu = [[SFApplication createWindowMenu] autorelease];
[windowItem setSubmenu:windowMenu];
[NSApp setWindowsMenu:windowMenu];
}
////////////////////////////////////////////////////////
+(NSMenu *)createAppleMenu
{
// Apple menu is as follow:
//
// AppName >
// About AppName
// --------------------
// Preferences... [greyed]
// --------------------
// Services >
// / default empty menu /
// --------------------
// Hide AppName H
// Hide Others H
// Show All
// --------------------
// Quit AppName Q
NSString* appName = [SFApplication applicationName];
// APPLE MENU
NSMenu* appleMenu = [[NSMenu alloc] initWithTitle:@""];
// ABOUT
[appleMenu addItemWithTitle:[@"About " stringByAppendingString:appName]
action:@selector(orderFrontStandardAboutPanel:)
keyEquivalent:@""];
// SEPARATOR
[appleMenu addItem:[NSMenuItem separatorItem]];
// PREFERENCES
[appleMenu addItemWithTitle:@"Preferences…"
action:nil
keyEquivalent:@""];
// SEPARATOR
[appleMenu addItem:[NSMenuItem separatorItem]];
// SERVICES
NSMenu* serviceMenu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
NSMenuItem* serviceItem = [appleMenu addItemWithTitle:@"Services"
action:nil
keyEquivalent:@""];
[serviceItem setSubmenu:serviceMenu];
[NSApp setServicesMenu:serviceMenu];
// SEPARATOR
[appleMenu addItem:[NSMenuItem separatorItem]];
// HIDE
[appleMenu addItemWithTitle:[@"Hide " stringByAppendingString:appName]
action:@selector(hide:)
keyEquivalent:@"h"];
// HIDE OTHER
NSMenuItem* hideOtherItem = [appleMenu addItemWithTitle:@"Hide Others"
action:@selector(hideOtherApplications:)
keyEquivalent:@"h"];
[hideOtherItem setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask)];
// SHOW ALL
[appleMenu addItemWithTitle:@"Show All"
action:@selector(unhideAllApplications:)
keyEquivalent:@""];
// SEPARATOR
[appleMenu addItem:[NSMenuItem separatorItem]];
// QUIT
[appleMenu addItemWithTitle:[@"Quit " stringByAppendingString:appName]
action:@selector(terminate:)
keyEquivalent:@"q"];
return appleMenu;
}
////////////////////////////////////////////////////////
+(NSMenu *)createFileMenu
{
// The File menu is as follow:
//
// File >
// Close W
// FILE MENU
NSMenu* fileMenu = [[NSMenu alloc] initWithTitle:@"File"];
// CLOSE WINDOW
NSMenuItem* closeItem = [[NSMenuItem alloc] initWithTitle:@"Close Window"
action:@selector(performClose:)
keyEquivalent:@"w"];
[fileMenu addItem:closeItem];
[closeItem release];
return fileMenu;
}
////////////////////////////////////////////////////////
+(NSMenu *)createWindowMenu
{
// The Window menu is as follow:
//
// Window >
// Minimize M
// Zoom
// --------------------
// Bring All to Front
// WINDOW MENU
NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
// MINIMIZE
NSMenuItem* minimizeItem = [[NSMenuItem alloc] initWithTitle:@"Minimize"
action:@selector(performMiniaturize:)
keyEquivalent:@"m"];
[windowMenu addItem:minimizeItem];
[minimizeItem release];
// ZOOM
[windowMenu addItemWithTitle:@"Zoom"
action:@selector(performZoom:)
keyEquivalent:@""];
// SEPARATOR
[windowMenu addItem:[NSMenuItem separatorItem]];
// BRING ALL TO FRONT
[windowMenu addItemWithTitle:@"Bring All to Front"
action:@selector(bringAllToFront:)
keyEquivalent:@""];
return windowMenu;
}
////////////////////////////////////////////////////////
+(NSString *)applicationName
{
// First, try localized name
NSString* appName = [[[NSBundle mainBundle] localizedInfoDictionary] objectForKey:@"CFBundleDisplayName"];
// Then, try non-localized name
if (appName == nil || [appName length] == 0) {
appName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
}
// Finally, fallback to the process info
if (appName == nil || [appName length] == 0) {
appName = [[NSProcessInfo processInfo] processName];
}
return appName;
}
////////////////////////////////////////////////////////
-(void)bringAllToFront:(id)sender
{
[[NSApp windows] makeObjectsPerformSelector:@selector(orderFrontRegardless)];
}
////////////////////////////////////////////////////////
-(void)sendEvent:(NSEvent *)anEvent -(void)sendEvent:(NSEvent *)anEvent
{ {
// Fullscreen windows have a strange behaviour with key up. To make // Fullscreen windows have a strange behaviour with key up. To make

View File

@ -0,0 +1,42 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2013 Marco Antognini (antognini.marco@gmail.com),
// Laurent Gomila (laurent.gom@gmail.com),
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
////////////////////////////////////////////////////////////
/// \brief Process some application specific events
///
////////////////////////////////////////////////////////////
@interface SFApplicationDelegate : NSObject <NSApplicationDelegate>
-(NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
-(BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication;
@end

View File

@ -0,0 +1,52 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2013 Marco Antognini (antognini.marco@gmail.com),
// Laurent Gomila (laurent.gom@gmail.com),
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#import <SFML/Window/OSX/SFApplicationDelegate.h>
////////////////////////////////////////////////////////////
@implementation SFApplicationDelegate
////////////////////////////////////////////////////////////
-(NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
// TODO generate close event for each SFML window
[NSApp makeWindowsPerform:@selector(sfClose) inOrder:NO];
return NSTerminateCancel;
}
////////////////////////////////////////////////////////////
-(BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
{
return YES;
}
@end

View File

@ -32,9 +32,7 @@
/// \brief Here we redefine some methods to allow grabing fullscreen events. /// \brief Here we redefine some methods to allow grabing fullscreen events.
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
@interface SFWindow : NSWindow { @interface SFWindow : NSWindow
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// These two methods must return YES to grab fullscreen events. /// These two methods must return YES to grab fullscreen events.
@ -45,4 +43,24 @@
-(BOOL)acceptsFirstResponder; -(BOOL)acceptsFirstResponder;
-(BOOL)canBecomeKeyWindow; -(BOOL)canBecomeKeyWindow;
////////////////////////////////////////////////////////////
/// Override default implementation of keyDown: to prevent
/// system alert
///
////////////////////////////////////////////////////////////
-(void)keyDown:(NSEvent *)theEvent;
@end
@interface NSWindow (SFML)
////////////////////////////////////////////////////////////
/// Proxy for performClose: for the app delegate
///
/// Always return nil
///
////////////////////////////////////////////////////////////
-(id)sfClose;
@end @end

View File

@ -44,6 +44,7 @@
return YES; return YES;
} }
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
-(void)keyDown:(NSEvent *)theEvent -(void)keyDown:(NSEvent *)theEvent
{ {
@ -56,4 +57,17 @@
// alert to be thrown everytime the user presses a key. // alert to be thrown everytime the user presses a key.
} }
@end
@implementation NSWindow (SFML)
////////////////////////////////////////////////////////////
-(id)sfClose
{
[self performClose:nil];
return nil;
}
@end @end

View File

@ -35,6 +35,7 @@
#import <SFML/Window/OSX/cpp_objc_conversion.h> #import <SFML/Window/OSX/cpp_objc_conversion.h>
#import <SFML/Window/OSX/AutoreleasePoolWrapper.h> #import <SFML/Window/OSX/AutoreleasePoolWrapper.h>
#import <SFML/Window/OSX/SFApplication.h> #import <SFML/Window/OSX/SFApplication.h>
#import <SFML/Window/OSX/SFApplicationDelegate.h>
namespace sf namespace sf
{ {
@ -137,6 +138,14 @@ void WindowImplCocoa::setUpProcess(void)
SetFrontProcess(&psn); SetFrontProcess(&psn);
} }
// Register an application delegate if there is none
if (![[SFApplication sharedApplication] delegate]) {
[NSApp setDelegate:[[SFApplicationDelegate alloc] init]];
}
// Create menus for the application (before finishing launching!)
[SFApplication setUpMenuBar];
// Tell the application to stop bouncing in the Dock. // Tell the application to stop bouncing in the Dock.
[[SFApplication sharedApplication] finishLaunching]; [[SFApplication sharedApplication] finishLaunching];
// NOTE : This last call won't harm anything even if SFML window was // NOTE : This last call won't harm anything even if SFML window was