mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-09-22 13:01:05 +03:00
Merge branch 'develop' into feature/ecode-settings-panel
# Conflicts: # src/tools/ecode/settingsmenu.cpp
This commit is contained in:
@@ -11,6 +11,8 @@
|
||||
|
||||
namespace EE { namespace UI {
|
||||
|
||||
enum class MenuBarRole : Uint8 { Normal, Window, Help };
|
||||
|
||||
class EE_API UIMenu : public UIWidget {
|
||||
public:
|
||||
static UIMenu* New();
|
||||
@@ -26,8 +28,6 @@ class EE_API UIMenu : public UIWidget {
|
||||
|
||||
UIMenuItem* add( const String& text, DrawablePtr icon = {}, const String& shortcutText = "" );
|
||||
|
||||
UIWidget* add( UIWidget* widget );
|
||||
|
||||
UIMenuSeparator* addSeparator();
|
||||
|
||||
UIMenuCheckBox* addCheckBox( const String& text, const bool& active = false,
|
||||
@@ -35,8 +35,7 @@ class EE_API UIMenu : public UIWidget {
|
||||
|
||||
UIMenuRadioButton* addRadioButton( const String& text, const bool& active = false );
|
||||
|
||||
UIMenuSubMenu* addSubMenu( const String& text, DrawablePtr icon = {},
|
||||
UIMenu* subMenu = NULL );
|
||||
UIMenuSubMenu* addSubMenu( const String& text, DrawablePtr icon = {}, UIMenu* subMenu = NULL );
|
||||
|
||||
UIWidget* getItem( const Uint32& index );
|
||||
|
||||
@@ -88,6 +87,14 @@ class EE_API UIMenu : public UIWidget {
|
||||
|
||||
const Clock& getInactiveTime() const;
|
||||
|
||||
void notifyMenuWillShow();
|
||||
|
||||
void notifyMenuDidHide();
|
||||
|
||||
MenuBarRole getMenuBarRole() const;
|
||||
|
||||
UIMenu* setMenuBarRole( MenuBarRole role );
|
||||
|
||||
protected:
|
||||
friend class UIMenuItem;
|
||||
friend class UIMenuCheckBox;
|
||||
@@ -103,6 +110,7 @@ class EE_API UIMenu : public UIWidget {
|
||||
UIWidget* mItemSelected;
|
||||
Uint32 mItemSelectedIndex;
|
||||
bool mResizing;
|
||||
MenuBarRole mMenuBarRole{ MenuBarRole::Normal };
|
||||
UIWidget* mOwnerNode;
|
||||
Sizei mIconMinSize;
|
||||
UIMenu* mCurrentSubMenu{ nullptr };
|
||||
@@ -122,6 +130,8 @@ class EE_API UIMenu : public UIWidget {
|
||||
|
||||
void resizeMe();
|
||||
|
||||
UIWidget* add( UIWidget* widget );
|
||||
|
||||
UIMenuItem* createMenuItem( const String& text, DrawablePtr icon,
|
||||
const String& shortcutText = "" );
|
||||
|
||||
|
||||
@@ -5,9 +5,12 @@
|
||||
#include <eepp/ui/uipopupmenu.hpp>
|
||||
#include <eepp/ui/uiselectbutton.hpp>
|
||||
#include <eepp/ui/uiwidget.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace EE { namespace UI {
|
||||
|
||||
class PlatformMenuBar;
|
||||
|
||||
class EE_API UIMenuBar : public UIWidget {
|
||||
public:
|
||||
static UIMenuBar* New();
|
||||
@@ -56,6 +59,12 @@ class EE_API UIMenuBar : public UIWidget {
|
||||
|
||||
void showPrevMenu();
|
||||
|
||||
bool isGlobalMenuBarSupported() const;
|
||||
|
||||
UIMenuBar* setGlobalMenuBarEnabled( bool enabled );
|
||||
|
||||
bool isGlobalMenuBarEnabled() const;
|
||||
|
||||
protected:
|
||||
UIMenuBar();
|
||||
|
||||
@@ -65,6 +74,7 @@ class EE_API UIMenuBar : public UIWidget {
|
||||
UIPopUpMenu* mCurrentMenu;
|
||||
MenuBarList mButtons;
|
||||
UIPopUpMenu* mWaitingUp;
|
||||
std::unique_ptr<PlatformMenuBar> mPlatformMenuBar;
|
||||
|
||||
Uint32 getMenuIndex( UIPopUpMenu* menu );
|
||||
|
||||
@@ -83,6 +93,8 @@ class EE_API UIMenuBar : public UIWidget {
|
||||
void destroyMenus();
|
||||
|
||||
void autoHeight();
|
||||
|
||||
void syncGlobalMenuBar();
|
||||
};
|
||||
|
||||
}} // namespace EE::UI
|
||||
|
||||
@@ -23,6 +23,8 @@ class EE_API UIMenuCheckBox : public UIMenuItem {
|
||||
|
||||
void switchActive();
|
||||
|
||||
virtual void activate();
|
||||
|
||||
virtual bool applyProperty( const StyleSheetProperty& attribute );
|
||||
|
||||
virtual std::string getPropertyString( const PropertyDefinition* propertyDef,
|
||||
@@ -37,8 +39,6 @@ class EE_API UIMenuCheckBox : public UIMenuItem {
|
||||
|
||||
UIMenuCheckBox();
|
||||
|
||||
Uint32 onMessage( const NodeMessage* msg );
|
||||
|
||||
virtual void onStateChange();
|
||||
};
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
#ifndef EE_UICUIMENUITEM_HPP
|
||||
#define EE_UICUIMENUITEM_HPP
|
||||
|
||||
#include <eepp/ui/keyboardshortcut.hpp>
|
||||
#include <eepp/ui/uipushbutton.hpp>
|
||||
|
||||
namespace EE { namespace UI {
|
||||
|
||||
enum class MenuRole : Uint8 { NoRole, About, Preferences, Quit };
|
||||
|
||||
class EE_API UIMenuItem : public UIPushButton {
|
||||
public:
|
||||
typedef std::function<bool( UIMenuItem* item )> OnShouldCloseCb;
|
||||
@@ -19,8 +22,12 @@ class EE_API UIMenuItem : public UIPushButton {
|
||||
|
||||
virtual void setTheme( UITheme* Theme );
|
||||
|
||||
virtual void activate();
|
||||
|
||||
virtual UIMenuItem* setShortcutText( const String& text );
|
||||
|
||||
const KeyBindings::Shortcut& getShortcut() const;
|
||||
|
||||
UITextView* getShortcutView() const;
|
||||
|
||||
virtual UIWidget* getExtraInnerWidget() const;
|
||||
@@ -29,9 +36,15 @@ class EE_API UIMenuItem : public UIPushButton {
|
||||
|
||||
UIMenuItem* setOnShouldCloseCb( const OnShouldCloseCb& onShouldCloseCb );
|
||||
|
||||
MenuRole getMenuRole() const;
|
||||
|
||||
UIMenuItem* setMenuRole( MenuRole role );
|
||||
|
||||
protected:
|
||||
UITextView* mShortcutView;
|
||||
OnShouldCloseCb mOnShouldCloseCb;
|
||||
mutable KeyBindings::Shortcut mShortcut;
|
||||
MenuRole mMenuRole{ MenuRole::NoRole };
|
||||
|
||||
UIMenuItem();
|
||||
|
||||
@@ -47,8 +60,6 @@ class EE_API UIMenuItem : public UIPushButton {
|
||||
|
||||
virtual Uint32 onMouseLeave( const Vector2i& pos, const Uint32& flags );
|
||||
|
||||
virtual Uint32 onMouseClick( const Vector2i& pos, const Uint32& flags );
|
||||
|
||||
void createShortcutView();
|
||||
|
||||
void refreshShortcut();
|
||||
|
||||
@@ -23,6 +23,8 @@ class EE_API UIMenuRadioButton : public UIMenuItem {
|
||||
|
||||
void switchActive();
|
||||
|
||||
virtual void activate();
|
||||
|
||||
virtual bool applyProperty( const StyleSheetProperty& attribute );
|
||||
|
||||
virtual std::string getPropertyString( const PropertyDefinition* propertyDef,
|
||||
@@ -37,8 +39,6 @@ class EE_API UIMenuRadioButton : public UIMenuItem {
|
||||
|
||||
UIMenuRadioButton();
|
||||
|
||||
virtual Uint32 onMouseUp( const Vector2i& position, const Uint32& flags );
|
||||
|
||||
virtual void onStateChange();
|
||||
};
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ class EE_API UIMenuSubMenu : public UIMenuItem {
|
||||
|
||||
void showSubMenu();
|
||||
|
||||
void notifySubMenuWillShow();
|
||||
|
||||
const Time& getMouseOverTimeShowMenu() const;
|
||||
|
||||
void setMouseOverTimeShowMenu( const Time& maxTime );
|
||||
|
||||
16
premake4.lua
16
premake4.lua
@@ -691,7 +691,7 @@ function generate_os_links()
|
||||
elseif os.is_real("mingw64") then
|
||||
multiple_insert( os_links, { "opengl32", "glu32", "gdi32", "ws2_32", "winmm", "ole32", "uuid", "dwrite" } )
|
||||
elseif os.is_real("macosx") then
|
||||
multiple_insert( os_links, { "OpenGL.framework", "CoreFoundation.framework", "CoreText.framework" } )
|
||||
multiple_insert( os_links, { "eepp-macos-helper-static", "Cocoa.framework", "OpenGL.framework", "CoreFoundation.framework", "CoreText.framework" } )
|
||||
elseif os.is_real("freebsd") then
|
||||
multiple_insert( os_links, { "rt", "pthread", "GL" } )
|
||||
elseif os.is_real("haiku") then
|
||||
@@ -1610,6 +1610,20 @@ solution "eepp"
|
||||
end
|
||||
build_base_cpp_configuration( "languages-syntax-highlighting" )
|
||||
|
||||
if os.is_real("macosx") then
|
||||
project "eepp-macos-helper-static"
|
||||
kind "StaticLib"
|
||||
language "C++"
|
||||
set_targetdir("libs/" .. os.get_real() .. "/")
|
||||
includedirs { "include", "src" }
|
||||
files { "src/eepp/ui/platform/macos/macosmenubar.mm" }
|
||||
buildoptions { "-x objective-c++" }
|
||||
if not is_vs() then
|
||||
buildoptions{ "-std=c++20" }
|
||||
end
|
||||
build_base_cpp_configuration( "eepp-macos-helper" )
|
||||
end
|
||||
|
||||
-- Library
|
||||
if not _OPTIONS["disable-static-build"] then
|
||||
project "eepp-static"
|
||||
|
||||
18
premake5.lua
18
premake5.lua
@@ -656,7 +656,7 @@ function generate_os_links()
|
||||
elseif os.istarget("mingw32") then
|
||||
multiple_insert( os_links, { "opengl32", "glu32", "gdi32", "ws2_32", "winmm", "ole32", "uuid", "dwrite" } )
|
||||
elseif os.istarget("macosx") then
|
||||
multiple_insert( os_links, { "OpenGL.framework", "CoreFoundation.framework", "CoreText.framework" } )
|
||||
multiple_insert( os_links, { "eepp-macos-helper-static", "Cocoa.framework", "OpenGL.framework", "CoreFoundation.framework", "CoreText.framework" } )
|
||||
elseif os.istarget("bsd") then
|
||||
multiple_insert( os_links, { "rt", "pthread", "GL" } )
|
||||
elseif os.istarget("haiku") then
|
||||
@@ -1652,6 +1652,22 @@ workspace "eepp"
|
||||
filter { "action:export-compile-commands", "system:macosx" }
|
||||
buildoptions { "-std=c++20" }
|
||||
|
||||
if os.istarget("macosx") then
|
||||
project "eepp-macos-helper-static"
|
||||
kind "StaticLib"
|
||||
language "C++"
|
||||
cppdialect "C++20"
|
||||
incdirs { "include", "src" }
|
||||
files { "src/eepp/ui/platform/macos/macosmenubar.mm" }
|
||||
buildoptions { "-x objective-c++" }
|
||||
build_base_cpp_configuration( "eepp-macos-helper" )
|
||||
target_dir_lib( "" )
|
||||
filter "action:not vs*"
|
||||
buildoptions { "-Wall" }
|
||||
filter { "action:export-compile-commands", "system:macosx" }
|
||||
buildoptions { "-std=c++20" }
|
||||
end
|
||||
|
||||
-- Library
|
||||
if not _OPTIONS["disable-static-build"] then
|
||||
project "eepp-static"
|
||||
|
||||
@@ -1181,6 +1181,9 @@
|
||||
../../src/eepp/ui/uiloader.cpp
|
||||
../../src/eepp/ui/uimanager.cpp
|
||||
../../src/eepp/ui/uimenubar.cpp
|
||||
../../src/eepp/ui/platformmenubar.hpp
|
||||
../../src/eepp/ui/platformmenubar.cpp
|
||||
../../src/eepp/ui/platform/macos/macosmenubar.mm
|
||||
../../src/eepp/ui/uimenucheckbox.cpp
|
||||
../../src/eepp/ui/uimenu.cpp
|
||||
../../src/eepp/ui/uimenuitem.cpp
|
||||
|
||||
539
src/eepp/ui/platform/macos/macosmenubar.mm
Normal file
539
src/eepp/ui/platform/macos/macosmenubar.mm
Normal file
@@ -0,0 +1,539 @@
|
||||
#define Rect AppleRect
|
||||
#import <AppKit/AppKit.h>
|
||||
#undef Rect
|
||||
#undef BSD
|
||||
|
||||
#include <eepp/system/log.hpp>
|
||||
#include <eepp/ui/platformmenubar.hpp>
|
||||
#include <eepp/ui/uimenu.hpp>
|
||||
#include <eepp/ui/uimenubar.hpp>
|
||||
#include <eepp/ui/uimenucheckbox.hpp>
|
||||
#include <eepp/ui/uimenuitem.hpp>
|
||||
#include <eepp/ui/uimenuradiobutton.hpp>
|
||||
#include <eepp/ui/uimenuseparator.hpp>
|
||||
#include <eepp/ui/uimenusubmenu.hpp>
|
||||
#include <eepp/window/keycodes.hpp>
|
||||
#include <unordered_map>
|
||||
|
||||
using namespace EE;
|
||||
using namespace EE::UI;
|
||||
using namespace EE::Window;
|
||||
|
||||
#if __has_feature( objc_arc )
|
||||
#define EE_OBJC_RELEASE( object )
|
||||
#else
|
||||
#define EE_OBJC_RELEASE( object ) [object release]
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
NSString* toNSString( const String& string ) {
|
||||
const std::string utf8( string.toUtf8() );
|
||||
return [NSString stringWithUTF8String:utf8.c_str()];
|
||||
}
|
||||
|
||||
NSString* applicationName() {
|
||||
NSBundle* bundle = [NSBundle mainBundle];
|
||||
NSString* name = [bundle objectForInfoDictionaryKey:@"CFBundleDisplayName"];
|
||||
if ( nil == name || 0 == name.length )
|
||||
name = [bundle objectForInfoDictionaryKey:@"CFBundleName"];
|
||||
if ( nil == name || 0 == name.length )
|
||||
name = [[NSProcessInfo processInfo] processName];
|
||||
return nil != name && name.length > 0 ? name : @"Application";
|
||||
}
|
||||
|
||||
NSString* keyEquivalent( Keycode key ) {
|
||||
if ( key >= KEY_F1 && key <= KEY_F12 ) {
|
||||
unichar character = NSF1FunctionKey + ( key - KEY_F1 );
|
||||
return [NSString stringWithCharacters:&character length:1];
|
||||
}
|
||||
if ( key >= KEY_F13 && key <= KEY_F24 ) {
|
||||
unichar character = NSF13FunctionKey + ( key - KEY_F13 );
|
||||
return [NSString stringWithCharacters:&character length:1];
|
||||
}
|
||||
|
||||
unichar character = 0;
|
||||
switch ( key ) {
|
||||
case KEY_RETURN:
|
||||
case KEY_KP_ENTER:
|
||||
character = NSCarriageReturnCharacter;
|
||||
break;
|
||||
case KEY_ESCAPE:
|
||||
character = 0x1B;
|
||||
break;
|
||||
case KEY_TAB:
|
||||
character = NSTabCharacter;
|
||||
break;
|
||||
case KEY_BACKSPACE:
|
||||
character = NSBackspaceCharacter;
|
||||
break;
|
||||
case KEY_DELETE:
|
||||
character = NSDeleteFunctionKey;
|
||||
break;
|
||||
case KEY_INSERT:
|
||||
character = NSInsertFunctionKey;
|
||||
break;
|
||||
case KEY_HOME:
|
||||
character = NSHomeFunctionKey;
|
||||
break;
|
||||
case KEY_END:
|
||||
character = NSEndFunctionKey;
|
||||
break;
|
||||
case KEY_PAGEUP:
|
||||
character = NSPageUpFunctionKey;
|
||||
break;
|
||||
case KEY_PAGEDOWN:
|
||||
character = NSPageDownFunctionKey;
|
||||
break;
|
||||
case KEY_LEFT:
|
||||
character = NSLeftArrowFunctionKey;
|
||||
break;
|
||||
case KEY_RIGHT:
|
||||
character = NSRightArrowFunctionKey;
|
||||
break;
|
||||
case KEY_UP:
|
||||
character = NSUpArrowFunctionKey;
|
||||
break;
|
||||
case KEY_DOWN:
|
||||
character = NSDownArrowFunctionKey;
|
||||
break;
|
||||
default:
|
||||
if ( key > 0 && key < 0x80 )
|
||||
character = static_cast<unichar>( key );
|
||||
break;
|
||||
}
|
||||
|
||||
if ( 0 == character )
|
||||
return @"";
|
||||
character = [[[NSString stringWithCharacters:&character
|
||||
length:1] lowercaseString] characterAtIndex:0];
|
||||
return [NSString stringWithCharacters:&character length:1];
|
||||
}
|
||||
|
||||
NSEventModifierFlags modifierMask( Uint32 modifiers ) {
|
||||
NSEventModifierFlags mask = 0;
|
||||
if ( modifiers & KEYMOD_META )
|
||||
mask |= NSEventModifierFlagCommand;
|
||||
if ( modifiers & KEYMOD_ALT )
|
||||
mask |= NSEventModifierFlagOption;
|
||||
if ( modifiers & KEYMOD_CTRL )
|
||||
mask |= NSEventModifierFlagControl;
|
||||
if ( modifiers & KEYMOD_SHIFT )
|
||||
mask |= NSEventModifierFlagShift;
|
||||
return mask;
|
||||
}
|
||||
|
||||
struct NativeMenuSource {
|
||||
UIMenu* menu;
|
||||
UIMenuSubMenu* owner;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
@interface EEPPMenuBarBridge : NSObject <NSMenuDelegate> {
|
||||
@private
|
||||
UIMenuBar* _menuBar;
|
||||
NSMenu* _installedMainMenu;
|
||||
NSMenu* _previousMainMenu;
|
||||
NSMenu* _servicesMenu;
|
||||
NSMenu* _windowsMenu;
|
||||
NSMenu* _helpMenu;
|
||||
BOOL _capturedPreviousMainMenu;
|
||||
std::unordered_map<NSMenu*, NativeMenuSource> _menuMap;
|
||||
std::unordered_map<NSMenuItem*, UIMenuItem*> _itemMap;
|
||||
}
|
||||
|
||||
- (instancetype)initWithMenuBar:(UIMenuBar*)menuBar;
|
||||
- (void)uninstall;
|
||||
- (void)syncTopLevel;
|
||||
- (void)menuItemActivated:(NSMenuItem*)sender;
|
||||
|
||||
@end
|
||||
|
||||
@implementation EEPPMenuBarBridge
|
||||
|
||||
- (instancetype)initWithMenuBar:(UIMenuBar*)menuBar {
|
||||
self = [super init];
|
||||
if ( nil != self )
|
||||
_menuBar = menuBar;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self uninstall];
|
||||
#if !__has_feature( objc_arc )
|
||||
[super dealloc];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (UIMenuItem*)findItemWithRole:(MenuRole)role inMenu:(UIMenu*)menu {
|
||||
if ( nullptr == menu )
|
||||
return nullptr;
|
||||
for ( Uint32 i = 0; i < menu->getCount(); ++i ) {
|
||||
UIWidget* widget = menu->getItem( i );
|
||||
if ( widget->isType( UI_TYPE_MENUITEM ) ) {
|
||||
UIMenuItem* item = widget->asType<UIMenuItem>();
|
||||
if ( item->getMenuRole() == role )
|
||||
return item;
|
||||
}
|
||||
if ( widget->isType( UI_TYPE_MENUSUBMENU ) ) {
|
||||
UIMenuItem* found =
|
||||
[self findItemWithRole:role inMenu:widget->asType<UIMenuSubMenu>()->getSubMenu()];
|
||||
if ( nullptr != found )
|
||||
return found;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
- (UIMenuItem*)findItemWithRole:(MenuRole)role {
|
||||
if ( nullptr == _menuBar )
|
||||
return nullptr;
|
||||
for ( Uint32 i = 0; i < _menuBar->getButtonsCount(); ++i ) {
|
||||
UIMenuItem* item = [self findItemWithRole:role inMenu:_menuBar->getPopUpMenu( i )];
|
||||
if ( nullptr != item )
|
||||
return item;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
- (NSMenuItem*)mirroredItemWithTitle:(NSString*)title source:(UIMenuItem*)source {
|
||||
const KeyBindings::Shortcut& shortcut = source->getShortcut();
|
||||
NSMenuItem* item = [[NSMenuItem alloc] initWithTitle:title
|
||||
action:@selector( menuItemActivated: )
|
||||
keyEquivalent:keyEquivalent( shortcut.key )];
|
||||
[item setTarget:self];
|
||||
[item setEnabled:source->isEnabled()];
|
||||
[item setKeyEquivalentModifierMask:modifierMask( shortcut.mod )];
|
||||
_itemMap[item] = source;
|
||||
return item;
|
||||
}
|
||||
|
||||
- (void)addRoleItem:(UIMenuItem*)source
|
||||
title:(NSString*)title
|
||||
toMenu:(NSMenu*)menu
|
||||
defaultAction:(SEL)defaultAction {
|
||||
NSMenuItem* item;
|
||||
if ( nullptr != source ) {
|
||||
item = [self mirroredItemWithTitle:title source:source];
|
||||
if ( source->getMenuRole() == MenuRole::Quit ) {
|
||||
[item setKeyEquivalent:@"q"];
|
||||
[item setKeyEquivalentModifierMask:NSEventModifierFlagCommand];
|
||||
}
|
||||
} else {
|
||||
item = [[NSMenuItem alloc] initWithTitle:title action:defaultAction keyEquivalent:@""];
|
||||
[item setTarget:NSApp];
|
||||
}
|
||||
[menu addItem:item];
|
||||
EE_OBJC_RELEASE( item );
|
||||
}
|
||||
|
||||
- (void)buildApplicationMenuInMainMenu:(NSMenu*)mainMenu {
|
||||
NSString* appName = applicationName();
|
||||
NSMenu* appMenu = [[NSMenu alloc] initWithTitle:appName];
|
||||
[appMenu setAutoenablesItems:NO];
|
||||
|
||||
NSString* title = [@"About " stringByAppendingString:appName];
|
||||
[self addRoleItem:[self findItemWithRole:MenuRole::About]
|
||||
title:title
|
||||
toMenu:appMenu
|
||||
defaultAction:@selector( orderFrontStandardAboutPanel: )];
|
||||
|
||||
UIMenuItem* preferences = [self findItemWithRole:MenuRole::Preferences];
|
||||
if ( nullptr != preferences ) {
|
||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||
[self addRoleItem:preferences title:@"Settings…" toMenu:appMenu defaultAction:nil];
|
||||
}
|
||||
|
||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||
_servicesMenu = [[NSMenu alloc] initWithTitle:@"Services"];
|
||||
NSMenuItem* servicesItem = [[NSMenuItem alloc] initWithTitle:@"Services"
|
||||
action:nil
|
||||
keyEquivalent:@""];
|
||||
[servicesItem setSubmenu:_servicesMenu];
|
||||
[appMenu addItem:servicesItem];
|
||||
[NSApp setServicesMenu:_servicesMenu];
|
||||
EE_OBJC_RELEASE( servicesItem );
|
||||
EE_OBJC_RELEASE( _servicesMenu );
|
||||
|
||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||
title = [@"Hide " stringByAppendingString:appName];
|
||||
NSMenuItem* item = [[NSMenuItem alloc] initWithTitle:title
|
||||
action:@selector( hide: )
|
||||
keyEquivalent:@"h"];
|
||||
[item setTarget:NSApp];
|
||||
[appMenu addItem:item];
|
||||
EE_OBJC_RELEASE( item );
|
||||
|
||||
item = [[NSMenuItem alloc] initWithTitle:@"Hide Others"
|
||||
action:@selector( hideOtherApplications: )
|
||||
keyEquivalent:@"h"];
|
||||
[item setTarget:NSApp];
|
||||
[item setKeyEquivalentModifierMask:NSEventModifierFlagCommand | NSEventModifierFlagOption];
|
||||
[appMenu addItem:item];
|
||||
EE_OBJC_RELEASE( item );
|
||||
|
||||
item = [[NSMenuItem alloc] initWithTitle:@"Show All"
|
||||
action:@selector( unhideAllApplications: )
|
||||
keyEquivalent:@""];
|
||||
[item setTarget:NSApp];
|
||||
[appMenu addItem:item];
|
||||
EE_OBJC_RELEASE( item );
|
||||
|
||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||
title = [@"Quit " stringByAppendingString:appName];
|
||||
UIMenuItem* quit = [self findItemWithRole:MenuRole::Quit];
|
||||
if ( nullptr != quit ) {
|
||||
[self addRoleItem:quit title:title toMenu:appMenu defaultAction:nil];
|
||||
} else {
|
||||
item = [[NSMenuItem alloc] initWithTitle:title
|
||||
action:@selector( terminate: )
|
||||
keyEquivalent:@"q"];
|
||||
[item setTarget:NSApp];
|
||||
[appMenu addItem:item];
|
||||
EE_OBJC_RELEASE( item );
|
||||
}
|
||||
|
||||
NSMenuItem* appMenuItem = [[NSMenuItem alloc] initWithTitle:appName
|
||||
action:nil
|
||||
keyEquivalent:@""];
|
||||
[appMenuItem setSubmenu:appMenu];
|
||||
[mainMenu addItem:appMenuItem];
|
||||
EE_OBJC_RELEASE( appMenuItem );
|
||||
EE_OBJC_RELEASE( appMenu );
|
||||
}
|
||||
|
||||
- (void)removeMappingsForMenu:(NSMenu*)menu removeMenu:(BOOL)removeMenu {
|
||||
for ( NSMenuItem* item in [menu itemArray] ) {
|
||||
_itemMap.erase( item );
|
||||
if ( nil != item.submenu )
|
||||
[self removeMappingsForMenu:item.submenu removeMenu:YES];
|
||||
}
|
||||
if ( removeMenu )
|
||||
_menuMap.erase( menu );
|
||||
}
|
||||
|
||||
- (NSMenuItem*)createNativeItem:(UIWidget*)widget {
|
||||
if ( widget->isType( UI_TYPE_MENU_SEPARATOR ) )
|
||||
return [NSMenuItem separatorItem];
|
||||
if ( !widget->isType( UI_TYPE_MENUITEM ) ) {
|
||||
Log::warning( "Global menu bar: unsupported widget type %u skipped", widget->getType() );
|
||||
return nil;
|
||||
}
|
||||
|
||||
UIMenuItem* source = widget->asType<UIMenuItem>();
|
||||
if ( source->getMenuRole() != MenuRole::NoRole )
|
||||
return nil;
|
||||
|
||||
NSMenuItem* item;
|
||||
if ( widget->isType( UI_TYPE_MENUSUBMENU ) ) {
|
||||
item = [[NSMenuItem alloc] initWithTitle:toNSString( source->getText() )
|
||||
action:nil
|
||||
keyEquivalent:@""];
|
||||
[item setEnabled:source->isEnabled()];
|
||||
UIMenu* eeSubMenu = widget->asType<UIMenuSubMenu>()->getSubMenu();
|
||||
if ( nullptr != eeSubMenu ) {
|
||||
NSMenu* subMenu = [[NSMenu alloc] initWithTitle:toNSString( source->getText() )];
|
||||
[subMenu setAutoenablesItems:NO];
|
||||
[subMenu setDelegate:self];
|
||||
[item setSubmenu:subMenu];
|
||||
_menuMap[subMenu] = { eeSubMenu, widget->asType<UIMenuSubMenu>() };
|
||||
EE_OBJC_RELEASE( subMenu );
|
||||
}
|
||||
} else {
|
||||
item = [self mirroredItemWithTitle:toNSString( source->getText() ) source:source];
|
||||
if ( widget->isType( UI_TYPE_MENUCHECKBOX ) ) {
|
||||
[item setState:widget->asType<UIMenuCheckBox>()->isActive() ? NSControlStateValueOn
|
||||
: NSControlStateValueOff];
|
||||
} else if ( widget->isType( UI_TYPE_MENURADIOBUTTON ) ) {
|
||||
[item setState:widget->asType<UIMenuRadioButton>()->isActive()
|
||||
? NSControlStateValueOn
|
||||
: NSControlStateValueOff];
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
- (void)rebuildMenu:(NSMenu*)nativeMenu fromMenu:(UIMenu*)eeMenu {
|
||||
[self removeMappingsForMenu:nativeMenu removeMenu:NO];
|
||||
[nativeMenu removeAllItems];
|
||||
bool lastWasSeparator = true;
|
||||
for ( Uint32 i = 0; i < eeMenu->getCount(); ++i ) {
|
||||
UIWidget* widget = eeMenu->getItem( i );
|
||||
if ( !widget->isVisible() )
|
||||
continue;
|
||||
const bool isSeparator = widget->isType( UI_TYPE_MENU_SEPARATOR );
|
||||
if ( isSeparator && lastWasSeparator )
|
||||
continue;
|
||||
NSMenuItem* item = [self createNativeItem:widget];
|
||||
if ( nil == item )
|
||||
continue;
|
||||
[nativeMenu addItem:item];
|
||||
lastWasSeparator = isSeparator;
|
||||
if ( !isSeparator )
|
||||
EE_OBJC_RELEASE( item );
|
||||
}
|
||||
if ( lastWasSeparator && [nativeMenu numberOfItems] > 0 )
|
||||
[nativeMenu removeItemAtIndex:[nativeMenu numberOfItems] - 1];
|
||||
}
|
||||
|
||||
- (void)syncTopLevel {
|
||||
eeASSERT( [NSThread isMainThread] );
|
||||
if ( nullptr == _menuBar )
|
||||
return;
|
||||
|
||||
if ( nil == NSApp )
|
||||
[NSApplication sharedApplication];
|
||||
if ( !_capturedPreviousMainMenu ) {
|
||||
_previousMainMenu = [NSApp mainMenu];
|
||||
#if !__has_feature( objc_arc )
|
||||
[_previousMainMenu retain];
|
||||
#endif
|
||||
_capturedPreviousMainMenu = YES;
|
||||
}
|
||||
|
||||
if ( nil != _installedMainMenu ) {
|
||||
[self removeMappingsForMenu:_installedMainMenu removeMenu:YES];
|
||||
_itemMap.clear();
|
||||
_menuMap.clear();
|
||||
EE_OBJC_RELEASE( _installedMainMenu );
|
||||
}
|
||||
_installedMainMenu = [[NSMenu alloc] initWithTitle:@""];
|
||||
[_installedMainMenu setAutoenablesItems:NO];
|
||||
[self buildApplicationMenuInMainMenu:_installedMainMenu];
|
||||
|
||||
_windowsMenu = nil;
|
||||
_helpMenu = nil;
|
||||
for ( Uint32 i = 0; i < _menuBar->getButtonsCount(); ++i ) {
|
||||
UIPopUpMenu* eeMenu = _menuBar->getPopUpMenu( i );
|
||||
if ( nullptr == eeMenu )
|
||||
continue;
|
||||
NSString* title = toNSString( _menuBar->getButton( i )->getText() );
|
||||
NSMenu* nativeMenu = [[NSMenu alloc] initWithTitle:title];
|
||||
[nativeMenu setAutoenablesItems:NO];
|
||||
[nativeMenu setDelegate:self];
|
||||
_menuMap[nativeMenu] = { eeMenu, nullptr };
|
||||
|
||||
NSMenuItem* topLevelItem = [[NSMenuItem alloc] initWithTitle:title
|
||||
action:nil
|
||||
keyEquivalent:@""];
|
||||
[topLevelItem setSubmenu:nativeMenu];
|
||||
[_installedMainMenu addItem:topLevelItem];
|
||||
|
||||
if ( eeMenu->getMenuBarRole() == MenuBarRole::Window )
|
||||
_windowsMenu = nativeMenu;
|
||||
else if ( eeMenu->getMenuBarRole() == MenuBarRole::Help )
|
||||
_helpMenu = nativeMenu;
|
||||
|
||||
EE_OBJC_RELEASE( topLevelItem );
|
||||
EE_OBJC_RELEASE( nativeMenu );
|
||||
}
|
||||
|
||||
[NSApp setMainMenu:_installedMainMenu];
|
||||
[NSApp setWindowsMenu:_windowsMenu];
|
||||
[NSApp setHelpMenu:_helpMenu];
|
||||
}
|
||||
|
||||
- (void)uninstall {
|
||||
eeASSERT( [NSThread isMainThread] );
|
||||
if ( nullptr == _menuBar && nil == _installedMainMenu )
|
||||
return;
|
||||
|
||||
_menuBar = nullptr;
|
||||
if ( [NSApp servicesMenu] == _servicesMenu )
|
||||
[NSApp setServicesMenu:nil];
|
||||
if ( [NSApp windowsMenu] == _windowsMenu )
|
||||
[NSApp setWindowsMenu:nil];
|
||||
if ( [NSApp helpMenu] == _helpMenu )
|
||||
[NSApp setHelpMenu:nil];
|
||||
if ( [NSApp mainMenu] == _installedMainMenu )
|
||||
[NSApp setMainMenu:_previousMainMenu];
|
||||
|
||||
if ( nil != _installedMainMenu )
|
||||
[self removeMappingsForMenu:_installedMainMenu removeMenu:YES];
|
||||
_itemMap.clear();
|
||||
_menuMap.clear();
|
||||
EE_OBJC_RELEASE( _installedMainMenu );
|
||||
EE_OBJC_RELEASE( _previousMainMenu );
|
||||
_installedMainMenu = nil;
|
||||
_previousMainMenu = nil;
|
||||
_servicesMenu = nil;
|
||||
_windowsMenu = nil;
|
||||
_helpMenu = nil;
|
||||
_capturedPreviousMainMenu = NO;
|
||||
}
|
||||
|
||||
- (void)menuNeedsUpdate:(NSMenu*)menu {
|
||||
eeASSERT( [NSThread isMainThread] );
|
||||
auto found = _menuMap.find( menu );
|
||||
if ( found == _menuMap.end() || nullptr == found->second.menu )
|
||||
return;
|
||||
NativeMenuSource source = found->second;
|
||||
if ( nullptr != source.owner ) {
|
||||
source.owner->notifySubMenuWillShow();
|
||||
found = _menuMap.find( menu );
|
||||
if ( found == _menuMap.end() || found->second.owner != source.owner )
|
||||
return;
|
||||
source.menu = source.owner->getSubMenu();
|
||||
if ( nullptr == source.menu )
|
||||
return;
|
||||
found->second.menu = source.menu;
|
||||
}
|
||||
UIMenu* eeMenu = source.menu;
|
||||
eeMenu->notifyMenuWillShow();
|
||||
found = _menuMap.find( menu );
|
||||
if ( found != _menuMap.end() && found->second.menu == eeMenu &&
|
||||
found->second.owner == source.owner )
|
||||
[self rebuildMenu:menu fromMenu:eeMenu];
|
||||
}
|
||||
|
||||
- (void)menuDidClose:(NSMenu*)menu {
|
||||
auto found = _menuMap.find( menu );
|
||||
if ( found != _menuMap.end() && nullptr != found->second.menu )
|
||||
found->second.menu->notifyMenuDidHide();
|
||||
}
|
||||
|
||||
- (void)menuItemActivated:(NSMenuItem*)sender {
|
||||
eeASSERT( [NSThread isMainThread] );
|
||||
auto found = _itemMap.find( sender );
|
||||
if ( found != _itemMap.end() && nullptr != found->second )
|
||||
found->second->activate();
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
namespace EE { namespace UI {
|
||||
|
||||
class MacOSMenuBar final : public PlatformMenuBar {
|
||||
public:
|
||||
~MacOSMenuBar() { uninstall(); }
|
||||
|
||||
void install( UIMenuBar* menuBar ) {
|
||||
eeASSERT( [NSThread isMainThread] );
|
||||
uninstall();
|
||||
mBridge = [[EEPPMenuBarBridge alloc] initWithMenuBar:menuBar];
|
||||
[mBridge syncTopLevel];
|
||||
}
|
||||
|
||||
void uninstall() {
|
||||
if ( nil == mBridge )
|
||||
return;
|
||||
[mBridge uninstall];
|
||||
EE_OBJC_RELEASE( mBridge );
|
||||
mBridge = nil;
|
||||
}
|
||||
|
||||
void syncTopLevel() {
|
||||
if ( nil != mBridge )
|
||||
[mBridge syncTopLevel];
|
||||
}
|
||||
|
||||
private:
|
||||
EEPPMenuBarBridge* mBridge{ nil };
|
||||
};
|
||||
|
||||
std::unique_ptr<PlatformMenuBar> createMacOSPlatformMenuBar() {
|
||||
return std::make_unique<MacOSMenuBar>();
|
||||
}
|
||||
|
||||
}} // namespace EE::UI
|
||||
25
src/eepp/ui/platformmenubar.cpp
Normal file
25
src/eepp/ui/platformmenubar.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include <eepp/ui/platformmenubar.hpp>
|
||||
|
||||
namespace EE { namespace UI {
|
||||
|
||||
#if EE_PLATFORM == EE_PLATFORM_MACOS
|
||||
std::unique_ptr<PlatformMenuBar> createMacOSPlatformMenuBar();
|
||||
#endif
|
||||
|
||||
bool PlatformMenuBar::isSupported() {
|
||||
#if EE_PLATFORM == EE_PLATFORM_MACOS
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::unique_ptr<PlatformMenuBar> PlatformMenuBar::create() {
|
||||
#if EE_PLATFORM == EE_PLATFORM_MACOS
|
||||
return createMacOSPlatformMenuBar();
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
}} // namespace EE::UI
|
||||
28
src/eepp/ui/platformmenubar.hpp
Normal file
28
src/eepp/ui/platformmenubar.hpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef EE_UI_PLATFORMMENUBAR_HPP
|
||||
#define EE_UI_PLATFORMMENUBAR_HPP
|
||||
|
||||
#include <eepp/config.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace EE { namespace UI {
|
||||
|
||||
class UIMenuBar;
|
||||
|
||||
class PlatformMenuBar {
|
||||
public:
|
||||
virtual ~PlatformMenuBar() = default;
|
||||
|
||||
virtual void install( UIMenuBar* menuBar ) = 0;
|
||||
|
||||
virtual void uninstall() = 0;
|
||||
|
||||
virtual void syncTopLevel() = 0;
|
||||
|
||||
static bool isSupported();
|
||||
|
||||
static std::unique_ptr<PlatformMenuBar> create();
|
||||
};
|
||||
|
||||
}} // namespace EE::UI
|
||||
|
||||
#endif
|
||||
@@ -305,8 +305,13 @@ Uint32 UIMenu::onMessage( const NodeMessage* msg ) {
|
||||
}
|
||||
case NodeMessage::MouseUp: {
|
||||
if ( msg->getSender()->getParent() == this && ( msg->getFlags() & EE_BUTTONS_LRM ) ) {
|
||||
Event itemEvent( msg->getSender(), Event::OnItemClicked );
|
||||
sendEvent( &itemEvent );
|
||||
if ( msg->getSender()->isType( UI_TYPE_MENUITEM ) &&
|
||||
!msg->getSender()->isType( UI_TYPE_MENUSUBMENU ) ) {
|
||||
msg->getSender()->asType<UIMenuItem>()->activate();
|
||||
} else {
|
||||
Event itemEvent( msg->getSender(), Event::OnItemClicked );
|
||||
sendEvent( &itemEvent );
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
@@ -386,18 +391,41 @@ void UIMenu::resizeMe() {
|
||||
}
|
||||
|
||||
bool UIMenu::show() {
|
||||
const bool notify = !isVisible();
|
||||
setEnabled( true );
|
||||
setVisible( true );
|
||||
if ( notify )
|
||||
notifyMenuWillShow();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UIMenu::hide() {
|
||||
const bool notify = isVisible();
|
||||
setEnabled( false );
|
||||
setVisible( false );
|
||||
safeHide();
|
||||
if ( notify )
|
||||
notifyMenuDidHide();
|
||||
return true;
|
||||
}
|
||||
|
||||
void UIMenu::notifyMenuWillShow() {
|
||||
sendCommonEvent( Event::OnMenuShow );
|
||||
}
|
||||
|
||||
void UIMenu::notifyMenuDidHide() {
|
||||
sendCommonEvent( Event::OnMenuHide );
|
||||
}
|
||||
|
||||
MenuBarRole UIMenu::getMenuBarRole() const {
|
||||
return mMenuBarRole;
|
||||
}
|
||||
|
||||
UIMenu* UIMenu::setMenuBarRole( MenuBarRole role ) {
|
||||
mMenuBarRole = role;
|
||||
return this;
|
||||
}
|
||||
|
||||
void UIMenu::safeHide() {
|
||||
if ( mOwnerNode && mOwnerNode->isType( UI_TYPE_MENUSUBMENU ) ) {
|
||||
UIMenu* menu = mOwnerNode->getParent()->asType<UIMenu>();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <eepp/graphics/textureregion.hpp>
|
||||
#include <eepp/scene/scenemanager.hpp>
|
||||
#include <eepp/ui/css/propertydefinition.hpp>
|
||||
#include <eepp/ui/platformmenubar.hpp>
|
||||
#include <eepp/ui/uimenubar.hpp>
|
||||
#include <eepp/ui/uiscenenode.hpp>
|
||||
#include <eepp/ui/uithememanager.hpp>
|
||||
@@ -88,6 +89,7 @@ void UIMenuBar::showPrevMenu() {
|
||||
}
|
||||
|
||||
UIMenuBar::~UIMenuBar() {
|
||||
setGlobalMenuBarEnabled( false );
|
||||
destroyMenus();
|
||||
}
|
||||
|
||||
@@ -153,6 +155,7 @@ void UIMenuBar::addMenuButton( const String& buttonText, UIPopUpMenu* menu ) {
|
||||
button->setThemeSkin( mTheme, "menubarbutton" );
|
||||
|
||||
refreshButtons();
|
||||
syncGlobalMenuBar();
|
||||
}
|
||||
|
||||
void UIMenuBar::setTheme( UITheme* theme ) {
|
||||
@@ -176,6 +179,7 @@ void UIMenuBar::removeMenuButton( const String& buttonText ) {
|
||||
it->second->close();
|
||||
mButtons.erase( it );
|
||||
refreshButtons();
|
||||
syncGlobalMenuBar();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -208,9 +212,38 @@ UIPopUpMenu* UIMenuBar::getPopUpMenu( const Uint32& index ) const {
|
||||
UIMenuBar* UIMenuBar::setPopUpMenu( const Uint32& index, UIPopUpMenu* menu ) {
|
||||
eeASSERT( index < mButtons.size() );
|
||||
mButtons[index].second = menu;
|
||||
syncGlobalMenuBar();
|
||||
return this;
|
||||
}
|
||||
|
||||
bool UIMenuBar::isGlobalMenuBarSupported() const {
|
||||
return PlatformMenuBar::isSupported();
|
||||
}
|
||||
|
||||
UIMenuBar* UIMenuBar::setGlobalMenuBarEnabled( bool enabled ) {
|
||||
if ( enabled == isGlobalMenuBarEnabled() )
|
||||
return this;
|
||||
|
||||
if ( enabled ) {
|
||||
mPlatformMenuBar = PlatformMenuBar::create();
|
||||
if ( mPlatformMenuBar )
|
||||
mPlatformMenuBar->install( this );
|
||||
} else if ( mPlatformMenuBar ) {
|
||||
mPlatformMenuBar->uninstall();
|
||||
mPlatformMenuBar.reset();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
bool UIMenuBar::isGlobalMenuBarEnabled() const {
|
||||
return nullptr != mPlatformMenuBar;
|
||||
}
|
||||
|
||||
void UIMenuBar::syncGlobalMenuBar() {
|
||||
if ( mPlatformMenuBar )
|
||||
mPlatformMenuBar->syncTopLevel();
|
||||
}
|
||||
|
||||
size_t UIMenuBar::getButtonsCount() const {
|
||||
return mButtons.size();
|
||||
}
|
||||
|
||||
@@ -106,15 +106,11 @@ void UIMenuCheckBox::switchActive() {
|
||||
setActive( !mActive );
|
||||
}
|
||||
|
||||
Uint32 UIMenuCheckBox::onMessage( const NodeMessage* msg ) {
|
||||
switch ( msg->getMsg() ) {
|
||||
case NodeMessage::MouseUp: {
|
||||
if ( msg->getFlags() & EE_BUTTON_LMASK )
|
||||
switchActive();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
void UIMenuCheckBox::activate() {
|
||||
if ( !isEnabled() )
|
||||
return;
|
||||
switchActive();
|
||||
UIMenuItem::activate();
|
||||
}
|
||||
|
||||
void UIMenuCheckBox::onStateChange() {
|
||||
|
||||
@@ -36,7 +36,25 @@ void UIMenuItem::setTheme( UITheme* Theme ) {
|
||||
onThemeLoaded();
|
||||
}
|
||||
|
||||
void UIMenuItem::activate() {
|
||||
if ( !isEnabled() || nullptr == getParent() || !getParent()->isType( UI_TYPE_MENU ) )
|
||||
return;
|
||||
|
||||
UIMenu* menu = getParent()->asType<UIMenu>();
|
||||
Input* input = nullptr != getUISceneNode() ? getInput() : nullptr;
|
||||
if ( ( nullptr == input || !input->isShiftPressed() ) &&
|
||||
( !mOnShouldCloseCb || mOnShouldCloseCb( this ) ) ) {
|
||||
menu->backpropagateHide();
|
||||
}
|
||||
|
||||
Event itemEvent( this, Event::OnItemClicked );
|
||||
menu->sendEvent( &itemEvent );
|
||||
}
|
||||
|
||||
UIMenuItem* UIMenuItem::setShortcutText( const String& text ) {
|
||||
mShortcut = {};
|
||||
if ( !text.empty() && nullptr != getUISceneNode() )
|
||||
mShortcut = KeyBindings::toShortcut( getInput(), text.toUtf8() );
|
||||
if ( !text.empty() )
|
||||
createShortcutView();
|
||||
if ( mShortcutView )
|
||||
@@ -44,6 +62,14 @@ UIMenuItem* UIMenuItem::setShortcutText( const String& text ) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const KeyBindings::Shortcut& UIMenuItem::getShortcut() const {
|
||||
if ( mShortcut.empty() && nullptr != mShortcutView && !mShortcutView->getText().empty() &&
|
||||
nullptr != getUISceneNode() ) {
|
||||
mShortcut = KeyBindings::toShortcut( getInput(), mShortcutView->getText().toUtf8() );
|
||||
}
|
||||
return mShortcut;
|
||||
}
|
||||
|
||||
UITextView* UIMenuItem::getShortcutView() const {
|
||||
return mShortcutView;
|
||||
}
|
||||
@@ -75,14 +101,6 @@ Uint32 UIMenuItem::onMouseLeave( const Vector2i& pos, const Uint32& flags ) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
Uint32 UIMenuItem::onMouseClick( const Vector2i&, const Uint32& flags ) {
|
||||
if ( !getInput()->isShiftPressed() && ( flags & EE_BUTTON_LMASK ) &&
|
||||
( !mOnShouldCloseCb || mOnShouldCloseCb( this ) ) ) {
|
||||
getParent()->asType<UIMenu>()->backpropagateHide();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
UIWidget* UIMenuItem::getExtraInnerWidget() const {
|
||||
return mShortcutView;
|
||||
}
|
||||
@@ -96,6 +114,15 @@ UIMenuItem* UIMenuItem::setOnShouldCloseCb( const OnShouldCloseCb& onShouldClose
|
||||
return this;
|
||||
}
|
||||
|
||||
MenuRole UIMenuItem::getMenuRole() const {
|
||||
return mMenuRole;
|
||||
}
|
||||
|
||||
UIMenuItem* UIMenuItem::setMenuRole( MenuRole role ) {
|
||||
mMenuRole = role;
|
||||
return this;
|
||||
}
|
||||
|
||||
void UIMenuItem::createShortcutView() {
|
||||
if ( mShortcutView )
|
||||
return;
|
||||
|
||||
@@ -119,13 +119,12 @@ void UIMenuRadioButton::switchActive() {
|
||||
setActive( !mActive );
|
||||
}
|
||||
|
||||
Uint32 UIMenuRadioButton::onMouseUp( const Vector2i& Pos, const Uint32& Flags ) {
|
||||
UIMenuItem::onMouseUp( Pos, Flags );
|
||||
|
||||
if ( getParent()->isVisible() && ( Flags & EE_BUTTONS_LRM ) && !mActive )
|
||||
switchActive();
|
||||
|
||||
return 1;
|
||||
void UIMenuRadioButton::activate() {
|
||||
if ( !isEnabled() )
|
||||
return;
|
||||
if ( !mActive )
|
||||
setActive( true );
|
||||
UIMenuItem::activate();
|
||||
}
|
||||
|
||||
void UIMenuRadioButton::onStateChange() {
|
||||
|
||||
@@ -97,7 +97,9 @@ UIMenu* UIMenuSubMenu::getSubMenu() const {
|
||||
}
|
||||
|
||||
void UIMenuSubMenu::showSubMenu() {
|
||||
sendCommonEvent( Event::OnMenuShow );
|
||||
notifySubMenuWillShow();
|
||||
if ( nullptr == mSubMenu )
|
||||
return;
|
||||
UIMenu* menu = getParent()->asType<UIMenu>();
|
||||
mSubMenu->setParent( menu->getParent() );
|
||||
Vector2f pos = getPixelsPosition();
|
||||
@@ -114,6 +116,10 @@ void UIMenuSubMenu::showSubMenu() {
|
||||
}
|
||||
}
|
||||
|
||||
void UIMenuSubMenu::notifySubMenuWillShow() {
|
||||
sendCommonEvent( Event::OnMenuShow );
|
||||
}
|
||||
|
||||
Uint32 UIMenuSubMenu::onMouseOver( const Vector2i& pos, const Uint32& flags ) {
|
||||
if ( nullptr == mCurWait ) {
|
||||
mCurWait = Actions::Runnable::New(
|
||||
|
||||
@@ -52,7 +52,7 @@ bool UIPopUpMenu::show() {
|
||||
getUISceneNode()->getUIThemeManager()->getWidgetsFadeOutTime() ),
|
||||
Actions::Spawn::New( Actions::Enable::New(), Actions::Visible::New( true ) ) ) );
|
||||
}
|
||||
sendCommonEvent( Event::OnMenuShow );
|
||||
notifyMenuWillShow();
|
||||
setFocus();
|
||||
return true;
|
||||
}
|
||||
@@ -92,7 +92,7 @@ bool UIPopUpMenu::hide() {
|
||||
close();
|
||||
}
|
||||
safeHide();
|
||||
sendCommonEvent( Event::OnMenuHide );
|
||||
notifyMenuDidHide();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
87
src/tests/unit_tests/uimenu_tests.cpp
Normal file
87
src/tests/unit_tests/uimenu_tests.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
#include "utest.h"
|
||||
|
||||
#include <eepp/system/filesystem.hpp>
|
||||
#include <eepp/system/sys.hpp>
|
||||
#include <eepp/ui/uiapplication.hpp>
|
||||
#include <eepp/ui/uimenu.hpp>
|
||||
#include <eepp/ui/uipopupmenu.hpp>
|
||||
#include <eepp/ui/uiscenenode.hpp>
|
||||
|
||||
using namespace EE;
|
||||
using namespace EE::System;
|
||||
using namespace EE::UI;
|
||||
using namespace EE::Window;
|
||||
|
||||
UTEST( UIMenu, SemanticActivationLifecycleAndRoles ) {
|
||||
UIApplication app(
|
||||
WindowSettings( 320, 240, "eepp - UIMenu Test", WindowStyle::Default,
|
||||
WindowBackend::Default, 32, {}, 1, false, true ),
|
||||
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) );
|
||||
UIMenu* menu = UIMenu::New();
|
||||
menu->setParent( app.getUI() );
|
||||
|
||||
int clicked = 0;
|
||||
int shown = 0;
|
||||
int hidden = 0;
|
||||
menu->on( Event::OnItemClicked, [&clicked]( const Event* ) { ++clicked; } );
|
||||
menu->on( Event::OnMenuShow, [&shown]( const Event* ) { ++shown; } );
|
||||
menu->on( Event::OnMenuHide, [&hidden]( const Event* ) { ++hidden; } );
|
||||
|
||||
UIMenuItem* item = menu->add( "Item" );
|
||||
EXPECT_EQ( item->getMenuRole(), MenuRole::NoRole );
|
||||
EXPECT_EQ( item->setMenuRole( MenuRole::About ), item );
|
||||
EXPECT_EQ( item->getMenuRole(), MenuRole::About );
|
||||
item->activate();
|
||||
EXPECT_EQ( clicked, 1 );
|
||||
item->setEnabled( false );
|
||||
item->activate();
|
||||
EXPECT_EQ( clicked, 1 );
|
||||
item->setEnabled( true );
|
||||
|
||||
menu->show();
|
||||
item->setOnShouldCloseCb( []( UIMenuItem* ) { return false; } );
|
||||
item->activate();
|
||||
EXPECT_TRUE( menu->isVisible() );
|
||||
EXPECT_EQ( clicked, 2 );
|
||||
item->setOnShouldCloseCb( {} );
|
||||
menu->hide();
|
||||
|
||||
UIMenuCheckBox* checkBox = menu->addCheckBox( "Check" );
|
||||
EXPECT_FALSE( checkBox->isActive() );
|
||||
checkBox->activate();
|
||||
EXPECT_TRUE( checkBox->isActive() );
|
||||
checkBox->activate();
|
||||
EXPECT_FALSE( checkBox->isActive() );
|
||||
EXPECT_EQ( clicked, 4 );
|
||||
|
||||
UIMenuRadioButton* radioA = menu->addRadioButton( "A", true );
|
||||
UIMenuRadioButton* radioB = menu->addRadioButton( "B" );
|
||||
radioB->activate();
|
||||
EXPECT_FALSE( radioA->isActive() );
|
||||
EXPECT_TRUE( radioB->isActive() );
|
||||
radioB->activate();
|
||||
EXPECT_TRUE( radioB->isActive() );
|
||||
EXPECT_EQ( clicked, 6 );
|
||||
|
||||
menu->notifyMenuWillShow();
|
||||
menu->notifyMenuDidHide();
|
||||
EXPECT_EQ( shown, 2 );
|
||||
EXPECT_EQ( hidden, 3 );
|
||||
|
||||
EXPECT_EQ( menu->getMenuBarRole(), MenuBarRole::Normal );
|
||||
EXPECT_EQ( menu->setMenuBarRole( MenuBarRole::Help ), menu );
|
||||
EXPECT_EQ( menu->getMenuBarRole(), MenuBarRole::Help );
|
||||
|
||||
UIPopUpMenu* childMenu = UIPopUpMenu::New();
|
||||
childMenu->setParent( app.getUI() );
|
||||
UIMenuSubMenu* subMenu = menu->addSubMenu( "Submenu", {}, childMenu );
|
||||
int subMenuShowStep = 0;
|
||||
subMenu->on( Event::OnMenuShow, [&subMenuShowStep]( const Event* ) {
|
||||
subMenuShowStep = 0 == subMenuShowStep ? 1 : -1;
|
||||
} );
|
||||
childMenu->on( Event::OnMenuShow, [&subMenuShowStep]( const Event* ) {
|
||||
subMenuShowStep = 1 == subMenuShowStep ? 2 : -1;
|
||||
} );
|
||||
subMenu->showSubMenu();
|
||||
EXPECT_EQ( subMenuShowStep, 2 );
|
||||
}
|
||||
@@ -4689,7 +4689,6 @@ void App::init( InitParameters& params ) {
|
||||
EE_PLATFORM == EE_PLATFORM_BSD
|
||||
|
||||
#if EE_PLATFORM == EE_PLATFORM_MACOS
|
||||
macOS_createApplicationMenus();
|
||||
macOS_enableScrollMomentum();
|
||||
macOS_removeTitleBarSeparator( mWindow->getWindowHandler() );
|
||||
#endif
|
||||
@@ -5144,6 +5143,8 @@ void App::init( InitParameters& params ) {
|
||||
|
||||
mSettings = std::make_unique<SettingsMenu>();
|
||||
mSettings->createSettingsMenu( this, mMenuBar );
|
||||
if ( mMenuBar->isGlobalMenuBarSupported() )
|
||||
mMenuBar->setGlobalMenuBarEnabled( true );
|
||||
|
||||
mSplitter->createEditorWithTabWidget( mBaseLayout );
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void macOS_createApplicationMenus();
|
||||
|
||||
void macOS_enableScrollMomentum();
|
||||
|
||||
void macOS_removeTitleBarSeparator( void* nsWindow );
|
||||
|
||||
@@ -3,67 +3,6 @@
|
||||
|
||||
#include "macos.hpp"
|
||||
|
||||
/* setAppleMenu disappeared from the headers in 10.4 */
|
||||
@interface NSApplication(NSAppleMenu)
|
||||
- (void)setAppleMenu:(NSMenu *)menu;
|
||||
@end
|
||||
|
||||
// Recreates the menubar replacing the default SDL menubar
|
||||
void macOS_createApplicationMenus() {
|
||||
NSString *appName;
|
||||
NSString *title;
|
||||
NSMenu *appleMenu;
|
||||
NSMenu *windowMenu;
|
||||
NSMenuItem *menuItem;
|
||||
NSMenu *mainMenu;
|
||||
|
||||
if (NSApp == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
mainMenu = [[NSApplication sharedApplication] mainMenu];
|
||||
[mainMenu removeAllItems];
|
||||
|
||||
appName = @"ecode";
|
||||
appleMenu = [[NSMenu alloc] initWithTitle:@""];
|
||||
|
||||
title = [@"About " stringByAppendingString:appName];
|
||||
[appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
|
||||
|
||||
[appleMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
[appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
|
||||
|
||||
[appleMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
title = [@"Quit " stringByAppendingString:appName];
|
||||
[appleMenu addItemWithTitle:title action:@selector(performClose:) keyEquivalent:@"q"];
|
||||
|
||||
menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
|
||||
[menuItem setSubmenu:appleMenu];
|
||||
|
||||
[[NSApp mainMenu] addItem:menuItem];
|
||||
|
||||
[NSApp setAppleMenu:appleMenu];
|
||||
|
||||
windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
|
||||
|
||||
menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(miniaturize:) keyEquivalent:@"m"];
|
||||
[menuItem setKeyEquivalentModifierMask:NSEventModifierFlagCommand];
|
||||
[windowMenu addItem:menuItem];
|
||||
|
||||
menuItem = [[NSMenuItem alloc] initWithTitle:@"Toggle Full Screen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"];
|
||||
[menuItem setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand];
|
||||
[windowMenu addItem:menuItem];
|
||||
|
||||
menuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
|
||||
[menuItem setSubmenu:windowMenu];
|
||||
|
||||
[[NSApp mainMenu] addItem:menuItem];
|
||||
|
||||
[NSApp setWindowsMenu:windowMenu];
|
||||
}
|
||||
|
||||
void macOS_enableScrollMomentum() {
|
||||
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"AppleMomentumScrollSupported"];
|
||||
}
|
||||
|
||||
@@ -177,6 +177,7 @@ void SettingsMenu::createSettingsMenu( App* app, UIMenuBar* menuBar ) {
|
||||
toolsMenuButton->on( Event::OnMenuShow, lazyBuildToolsMenu );
|
||||
|
||||
mWindowMenu = UIPopUpMenu::New();
|
||||
mWindowMenu->setMenuBarRole( MenuBarRole::Window );
|
||||
const auto lazyBuildWindowMenu = [this]( const Event* ) {
|
||||
if ( mWindowMenu->getCount() == 0 ) {
|
||||
createWindowMenu();
|
||||
@@ -192,6 +193,7 @@ void SettingsMenu::createSettingsMenu( App* app, UIMenuBar* menuBar ) {
|
||||
windowMenuButton->on( Event::OnMenuShow, lazyBuildWindowMenu );
|
||||
|
||||
mHelpMenu = UIPopUpMenu::New();
|
||||
mHelpMenu->setMenuBarRole( MenuBarRole::Help );
|
||||
const auto lazyBuildHelpMenu = [this]( const Event* ) {
|
||||
if ( mHelpMenu->getCount() == 0 ) {
|
||||
createHelpMenu();
|
||||
@@ -215,6 +217,7 @@ void SettingsMenu::createSettingsMenu( App* app, UIMenuBar* menuBar ) {
|
||||
->setId( "close-folder" );
|
||||
mSettingsMenu->addSeparator();
|
||||
mSettingsMenu->add( i18n( "quit", "Quit" ), findIcon( "quit" ), getKeybind( "close-app" ) )
|
||||
->setMenuRole( MenuRole::Quit )
|
||||
->setId( "close-app" );
|
||||
mSettingsButton = mUISceneNode->find<UIWidget>( "settings" );
|
||||
mSettingsButton->on( Event::MouseClick, [this]( const Event* ) { toggleSettingsMenu(); } );
|
||||
@@ -289,6 +292,7 @@ void SettingsMenu::createSettingsMenu( App* app, UIMenuBar* menuBar ) {
|
||||
onMenuShowEvent( mToolsMenu, toolsMenuButton, 6 );
|
||||
onMenuShowEvent( mWindowMenu, windowMenuButton, 7 );
|
||||
onMenuShowEvent( mHelpMenu, helpMenuButton, 8 );
|
||||
createHelpMenu();
|
||||
|
||||
updateMenu();
|
||||
|
||||
@@ -1071,6 +1075,7 @@ UIMenu* SettingsMenu::createHelpMenu() {
|
||||
mHelpMenu->add( i18n( "check_for_updates", "Check for Updates" ), findIcon( "refresh" ) )
|
||||
->setId( "check-for-updates" );
|
||||
mHelpMenu->add( i18n( "about_ecode", "About ecode" ), findIcon( "ecode" ) )
|
||||
->setMenuRole( MenuRole::About )
|
||||
->setId( "about-ecode" );
|
||||
mHelpMenu->on( Event::OnItemClicked,
|
||||
[this]( const Event* event ) { runCommand( event->getNode()->getId() ); } );
|
||||
|
||||
@@ -93,6 +93,6 @@ class App {
|
||||
UIConsole* mConsole{ nullptr };
|
||||
UIWelcomeScreen* mWelcomeScreen{ nullptr };
|
||||
bool mCursorVisible{ true };
|
||||
Texture::Filter mTextureFilter{ Texture::Filter::Linear };
|
||||
Texture::Filter mTextureFilter{ Texture::Filter::Nearest };
|
||||
std::unique_ptr<ConsoleCommands> mConsoleCommands;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user