Cocoa menu-bar implementation.
This commit is contained in:
parent
e1c1a28288
commit
ebfdebeb43
5 changed files with 321 additions and 4 deletions
|
@ -65,7 +65,8 @@ IF(APPLE)
|
|||
set(EVENT_INPUT_DEFAULT 1)
|
||||
|
||||
find_library(CORESERVICES_LIBRARY CoreServices)
|
||||
list(APPEND PLATFORM_LIBS ${CORESERVICES_LIBRARY})
|
||||
find_library(COCOA_LIBRARY Cocoa)
|
||||
list(APPEND PLATFORM_LIBS ${COCOA_LIBRARY} ${CORESERVICES_LIBRARY})
|
||||
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
# disabled while DBus / HAL / udev issues are decided
|
||||
|
|
|
@ -37,4 +37,9 @@ set(HEADERS
|
|||
FGColor.hxx
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
list(APPEND HEADERS FGCocoaMenuBar.hxx)
|
||||
list(APPEND SOURCES FGCocoaMenuBar.mm)
|
||||
endif()
|
||||
|
||||
flightgear_component(GUI "${SOURCES}" "${HEADERS}")
|
||||
|
|
65
src/GUI/FGCocoaMenuBar.hxx
Normal file
65
src/GUI/FGCocoaMenuBar.hxx
Normal file
|
@ -0,0 +1,65 @@
|
|||
// menubar.hxx - XML-configured menu bar.
|
||||
|
||||
#ifndef FG_COCOA_MENUBAR_HXX
|
||||
#define FG_COCOA_MENUBAR_HXX 1
|
||||
|
||||
#include <GUI/menubar.hxx>
|
||||
|
||||
#include <memory>
|
||||
|
||||
/**
|
||||
* XML-configured Cocoa menu bar.
|
||||
*
|
||||
* This class creates a menu bar from a tree of XML properties. These
|
||||
* properties are not part of the main FlightGear property tree, but
|
||||
* are read from a separate file ($FG_ROOT/gui/menubar.xml).
|
||||
*
|
||||
* WARNING: because PUI provides no easy way to attach user data to a
|
||||
* menu item, all menu item strings must be unique; otherwise, this
|
||||
* class will always use the first binding with any given name.
|
||||
*/
|
||||
class FGCocoaMenuBar : public FGMenuBar
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
FGCocoaMenuBar ();
|
||||
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~FGCocoaMenuBar ();
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the menu bar from $FG_ROOT/gui/menubar.xml
|
||||
*/
|
||||
virtual void init ();
|
||||
|
||||
/**
|
||||
* Make the menu bar visible.
|
||||
*/
|
||||
virtual void show ();
|
||||
|
||||
|
||||
/**
|
||||
* Make the menu bar invisible.
|
||||
*/
|
||||
virtual void hide ();
|
||||
|
||||
|
||||
/**
|
||||
* Test whether the menu bar is visible.
|
||||
*/
|
||||
virtual bool isVisible () const;
|
||||
|
||||
class CocoaMenuBarPrivate;
|
||||
private:
|
||||
std::auto_ptr<CocoaMenuBarPrivate> p;
|
||||
|
||||
};
|
||||
|
||||
#endif // __MENUBAR_HXX
|
231
src/GUI/FGCocoaMenuBar.mm
Normal file
231
src/GUI/FGCocoaMenuBar.mm
Normal file
|
@ -0,0 +1,231 @@
|
|||
#include "FGCocoaMenuBar.hxx"
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/props/props_io.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/structure/SGBinding.hxx>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using std::string;
|
||||
using std::map;
|
||||
using std::cout;
|
||||
|
||||
typedef std::map<NSMenuItem*, SGBindingList> MenuItemBindings;
|
||||
|
||||
@class CocoaMenuDelegate;
|
||||
|
||||
class FGCocoaMenuBar::CocoaMenuBarPrivate
|
||||
{
|
||||
public:
|
||||
CocoaMenuBarPrivate();
|
||||
~CocoaMenuBarPrivate();
|
||||
|
||||
bool labelIsSeparator(const std::string& s) const;
|
||||
void menuFromProps(NSMenu* menu, SGPropertyNode* menuNode);
|
||||
|
||||
void fireBindingsForItem(NSMenuItem* item);
|
||||
|
||||
public:
|
||||
CocoaMenuDelegate* delegate;
|
||||
|
||||
MenuItemBindings itemBindings;
|
||||
};
|
||||
|
||||
|
||||
@interface CocoaMenuDelegate : NSObject <NSMenuDelegate> {
|
||||
@private
|
||||
FGCocoaMenuBar::CocoaMenuBarPrivate* peer;
|
||||
}
|
||||
|
||||
@property (nonatomic, assign) FGCocoaMenuBar::CocoaMenuBarPrivate* peer;
|
||||
@end
|
||||
|
||||
@implementation CocoaMenuDelegate
|
||||
|
||||
@synthesize peer;
|
||||
|
||||
- (void) itemAction:(id) sender
|
||||
{
|
||||
peer->fireBindingsForItem((NSMenuItem*) sender);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static NSString* stdStringToCocoa(const string& s)
|
||||
{
|
||||
return [NSString stringWithUTF8String:s.c_str()];
|
||||
}
|
||||
|
||||
class EnabledListener : public SGPropertyChangeListener
|
||||
{
|
||||
public:
|
||||
EnabledListener(NSMenuItem* i) :
|
||||
item(i)
|
||||
{}
|
||||
|
||||
|
||||
virtual void valueChanged(SGPropertyNode *node)
|
||||
{
|
||||
BOOL b = node->getBoolValue();
|
||||
[item setEnabled:b];
|
||||
}
|
||||
|
||||
private:
|
||||
NSMenuItem* item;
|
||||
};
|
||||
|
||||
|
||||
|
||||
FGCocoaMenuBar::CocoaMenuBarPrivate::CocoaMenuBarPrivate()
|
||||
{
|
||||
delegate = [[CocoaMenuDelegate alloc] init];
|
||||
delegate.peer = this;
|
||||
}
|
||||
|
||||
FGCocoaMenuBar::CocoaMenuBarPrivate::~CocoaMenuBarPrivate()
|
||||
{
|
||||
[delegate release];
|
||||
}
|
||||
|
||||
bool FGCocoaMenuBar::CocoaMenuBarPrivate::labelIsSeparator(const std::string& s) const
|
||||
{
|
||||
for (unsigned int i=0; i<s.size(); ++i) {
|
||||
if (s[i] != '-') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FGCocoaMenuBar::CocoaMenuBarPrivate::menuFromProps(NSMenu* menu, SGPropertyNode* menuNode)
|
||||
{
|
||||
int index = 0;
|
||||
BOOST_FOREACH(SGPropertyNode_ptr n, menuNode->getChildren("item")) {
|
||||
if (!n->hasValue("enabled")) {
|
||||
n->setBoolValue("enabled", true);
|
||||
}
|
||||
|
||||
string l = n->getStringValue("label");
|
||||
string::size_type pos = l.find("(");
|
||||
if (pos != string::npos) {
|
||||
l = l.substr(0, pos);
|
||||
}
|
||||
|
||||
NSString* label = stdStringToCocoa(l);
|
||||
NSString* shortcut = @"";
|
||||
NSMenuItem* item;
|
||||
if (index >= [menu numberOfItems]) {
|
||||
if (labelIsSeparator(l)) {
|
||||
item = [NSMenuItem separatorItem];
|
||||
[menu addItem:item];
|
||||
} else {
|
||||
item = [menu addItemWithTitle:label action:nil keyEquivalent:shortcut];
|
||||
n->getNode("enabled")->addChangeListener(new EnabledListener(item));
|
||||
[item setTarget:delegate];
|
||||
[item setAction:@selector(itemAction:)];
|
||||
}
|
||||
} else {
|
||||
item = [menu itemAtIndex:index];
|
||||
[item setTitle:label];
|
||||
}
|
||||
|
||||
BOOL enabled = n->getBoolValue("enabled");
|
||||
[item setEnabled:enabled];
|
||||
|
||||
SGBindingList bl;
|
||||
BOOST_FOREACH(SGPropertyNode_ptr binding, n->getChildren("binding")) {
|
||||
// have to clone the bindings, since SGBinding takes ownership of the
|
||||
// passed in node. Seems like something is wrong here, but following the
|
||||
// PUI code for the moment.
|
||||
SGPropertyNode* cloned(new SGPropertyNode);
|
||||
copyProperties(binding, cloned);
|
||||
bl.push_back(new SGBinding(cloned, globals->get_props()));
|
||||
}
|
||||
|
||||
itemBindings[item] = bl;
|
||||
++index;
|
||||
} // of item iteration
|
||||
}
|
||||
|
||||
void FGCocoaMenuBar::CocoaMenuBarPrivate::fireBindingsForItem(NSMenuItem *item)
|
||||
{
|
||||
MenuItemBindings::iterator it = itemBindings.find(item);
|
||||
if (it == itemBindings.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
BOOST_FOREACH(SGSharedPtr<SGBinding> b, it->second) {
|
||||
b->fire();
|
||||
}
|
||||
}
|
||||
|
||||
FGCocoaMenuBar::FGCocoaMenuBar() :
|
||||
p(new CocoaMenuBarPrivate)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
FGCocoaMenuBar::~FGCocoaMenuBar()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void FGCocoaMenuBar::init()
|
||||
{
|
||||
NSMenu* mainBar = [[NSApplication sharedApplication] mainMenu];
|
||||
SGPropertyNode_ptr props = fgGetNode("/sim/menubar/default",true);
|
||||
|
||||
int index = 0;
|
||||
NSMenuItem* previousMenu = [mainBar itemAtIndex:0];
|
||||
if (![[previousMenu title] isEqualToString:@"FlightGear"]) {
|
||||
[previousMenu setTitle:@"FlightGear"];
|
||||
}
|
||||
|
||||
BOOST_FOREACH(SGPropertyNode_ptr n, props->getChildren("menu")) {
|
||||
NSString* label = stdStringToCocoa(n->getStringValue("label"));
|
||||
NSMenuItem* item = [mainBar itemWithTitle:label];
|
||||
NSMenu* menu;
|
||||
|
||||
if (!item) {
|
||||
NSInteger insertIndex = [mainBar indexOfItem:previousMenu] + 1;
|
||||
item = [mainBar insertItemWithTitle:label action:nil keyEquivalent:@"" atIndex:insertIndex];
|
||||
item.tag = index + 400;
|
||||
|
||||
menu = [[NSMenu alloc] init];
|
||||
menu.title = label;
|
||||
[menu setAutoenablesItems:NO];
|
||||
[mainBar setSubmenu:menu forItem:item];
|
||||
[menu autorelease];
|
||||
} else {
|
||||
menu = item.submenu;
|
||||
}
|
||||
|
||||
// synchronise menu with properties
|
||||
p->menuFromProps(menu, n);
|
||||
++index;
|
||||
previousMenu = item;
|
||||
}
|
||||
}
|
||||
|
||||
bool FGCocoaMenuBar::isVisible() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void FGCocoaMenuBar::show()
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
void FGCocoaMenuBar::hide()
|
||||
{
|
||||
// no-op
|
||||
}
|
|
@ -27,6 +27,11 @@
|
|||
#endif
|
||||
|
||||
#include "FGPUIMenuBar.hxx"
|
||||
|
||||
#if defined(SG_MAC)
|
||||
#include "FGCocoaMenuBar.hxx"
|
||||
#endif
|
||||
|
||||
#include "FGPUIDialog.hxx"
|
||||
#include "FGFontCache.hxx"
|
||||
#include "FGColor.hxx"
|
||||
|
@ -40,10 +45,14 @@ using std::string;
|
|||
|
||||
|
||||
|
||||
NewGUI::NewGUI ()
|
||||
: _menubar(new FGPUIMenuBar),
|
||||
_active_dialog(0)
|
||||
NewGUI::NewGUI () :
|
||||
_active_dialog(0)
|
||||
{
|
||||
#if defined(SG_MAC)
|
||||
_menubar.reset(new FGCocoaMenuBar);
|
||||
#else
|
||||
_menubar.reset(new FGPUIMenuBar);
|
||||
#endif
|
||||
}
|
||||
|
||||
NewGUI::~NewGUI ()
|
||||
|
@ -91,7 +100,13 @@ NewGUI::reset (bool reload)
|
|||
setStyle();
|
||||
|
||||
unbind();
|
||||
#if defined(SG_MAC)
|
||||
if (reload) {
|
||||
_menubar.reset(new FGCocoaMenuBar);
|
||||
}
|
||||
#else
|
||||
_menubar.reset(new FGPUIMenuBar);
|
||||
#endif
|
||||
|
||||
if (reload) {
|
||||
_dialog_props.clear();
|
||||
|
|
Loading…
Add table
Reference in a new issue