Merge branch 'develop' into feature/ecode-settings-panel

# Conflicts:
#	src/tools/ecode/settingsmenu.cpp
This commit is contained in:
Martín Lucas Golini
2026-08-24 19:20:47 -03:00
25 changed files with 885 additions and 106 deletions

View File

@@ -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 = "" );

View File

@@ -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

View File

@@ -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();
};

View File

@@ -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();

View File

@@ -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();
};

View File

@@ -27,6 +27,8 @@ class EE_API UIMenuSubMenu : public UIMenuItem {
void showSubMenu();
void notifySubMenuWillShow();
const Time& getMouseOverTimeShowMenu() const;
void setMouseOverTimeShowMenu( const Time& maxTime );

View File

@@ -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"

View File

@@ -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"

View File

@@ -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

View 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

View 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

View 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

View File

@@ -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>();

View File

@@ -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();
}

View File

@@ -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() {

View File

@@ -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;

View File

@@ -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() {

View File

@@ -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(

View File

@@ -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;

View 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 );
}

View File

@@ -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 );

View File

@@ -5,8 +5,6 @@
extern "C" {
#endif
void macOS_createApplicationMenus();
void macOS_enableScrollMomentum();
void macOS_removeTitleBarSeparator( void* nsWindow );

View File

@@ -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"];
}

View File

@@ -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() ); } );

View File

@@ -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;
};