OSX ios first version of application menubar support (#25863)
Unix builds / Ubuntu 18.04 wxGTK 3 compatible 3.0 (push) Has been cancelled
Unix builds / Ubuntu 24.04 wxGTK ASAN not compatible (push) Has been cancelled
Unix builds / Ubuntu 18.04 wxGTK UTF-8 (push) Has been cancelled
Unix builds / Ubuntu 18.04 wxQt (push) Has been cancelled
Unix builds / Ubuntu 18.04 wxX11 (push) Has been cancelled
Unix builds / Ubuntu 20.04 wxGTK 3 with clang (push) Has been cancelled
Unix builds / Ubuntu 22.04 wxGTK with wx containers (push) Has been cancelled
Unix builds / Ubuntu 24.04 wxGTK UBSAN (push) Has been cancelled
Unix builds / Ubuntu 18.04 wxDFB (push) Has been cancelled
Unix builds / Ubuntu 18.04 wxGTK 3 static with gcc 4.8 (push) Has been cancelled
Unix builds / Ubuntu 18.04 wxGTK 2 (push) Has been cancelled
CMake builds / Ubuntu 22.04 wxGTK 3 (push) Has been cancelled
CMake builds / MSW/MSVC wxMSW (push) Has been cancelled
CMake builds / MSW/Clang wxMSW (push) Has been cancelled
CMake builds / macOS latest wxOSX Ninja (push) Has been cancelled
CMake builds / macOS 14 wxOSX Xcode (push) Has been cancelled
CMake builds / macOS 14 wxIOS (push) Has been cancelled
CMake builds / MSW/MSVC wxQt 5.15 (push) Has been cancelled
CMake builds / MSW/MSVC wxQt 6.8 (push) Has been cancelled
Mac builds / wxMac ARM ASAN not compatible (push) Has been cancelled
Mac builds / wxMac Universal C++14 (push) Has been cancelled
Mac builds / wxiOS Simulator on Silicon Mac (push) Has been cancelled
Mac builds / wxiOS (push) Has been cancelled
Mac builds / wxMac Intel C++17 (push) Has been cancelled
Mac Xcode builds / iOS Simulator static (push) Has been cancelled
Mac Xcode builds / macOS dynamic Release (push) Has been cancelled
Mac Xcode builds / iOS static Debug (push) Has been cancelled
Code Checks / Check Spelling (push) Has been cancelled
Code Checks / Check Whitespace (push) Has been cancelled
Code Checks / Check Mixed EOL (push) Has been cancelled
Code Checks / Check C++ Style (push) Has been cancelled
Code Checks / Check All Headers In allheaders.h (push) Has been cancelled
No Response / no-response (push) Has been cancelled

* first version of app menubar support on ios, activate with wxUSE_MENUBAR = 1
This commit is contained in:
Stefan Csomor
2025-10-06 10:12:56 +02:00
committed by GitHub
parent 18a60c9875
commit f769029bdd
7 changed files with 136 additions and 199 deletions
+3 -1
View File
@@ -152,7 +152,9 @@ public:
virtual bool OSXOnShouldTerminate();
// before application terminates
virtual void OSXOnWillTerminate();
#if wxOSX_USE_IPHONE && wxUSE_MENUBAR
virtual void OSXOnBuildMenu(WX_NSObject menuBuilder);
#endif
private:
bool m_onInitResult;
bool m_inited;
+4
View File
@@ -151,6 +151,10 @@ public:
wxMenu *OSXGetAppleMenu() const { return m_appleMenu; }
#if wxOSX_USE_IPHONE
void OSXOnBuildMenu( WX_NSObject builder ) ;
#endif
static void SetAutoWindowMenu( bool enable ) { s_macAutoWindowMenu = enable ; }
static bool GetAutoWindowMenu() { return s_macAutoWindowMenu ; }
+10
View File
@@ -220,6 +220,16 @@ bool wxApp::OSXOnShouldTerminate()
ProcessEvent(event);
return !event.GetVeto();
}
#endif
#if wxOSX_USE_IPHONE && wxUSE_MENUBAR
void wxApp::OSXOnBuildMenu(WX_NSObject builder)
{
wxMenuBar::MacGetInstalledMenuBar()->OSXOnBuildMenu(builder);
}
#endif
#if wxDEBUG_LEVEL && wxOSX_USE_COCOA_OR_CARBON
+85 -183
View File
@@ -34,174 +34,24 @@
// other standard headers
// ----------------------
#include <string.h>
/*
@implementation wxUIMenu
- (id) initWithTitle:(NSString*) title
{
if ( self = [super initWithTitle:title] )
{
impl = nullptr;
}
return self;
}
- (void)setImplementation: (wxMenuImpl *) theImplementation
{
impl = theImplementation;
}
- (wxMenuImpl*) implementation
{
return impl;
}
@end
*/
// this is more compatible, as it is also called for command-key shortcuts
// and under 10.4, we are not getting a 'close' event however...
#define wxOSX_USE_NEEDSUPDATE_HOOK 1
@interface wxUIMenuController : NSObject // TODO wxIOS <UIMenuDelegate>
{
}
#if wxOSX_USE_NEEDSUPDATE_HOOK
- (void)menuNeedsUpdate:(UIMenu*)smenu;
#else
- (void)menuWillOpen:(UIMenu *)menu;
#endif
- (void)menuDidClose:(UIMenu *)menu;
- (void)menu:(UIMenu *)menu willHighlightItem:(UIMenuItem *)item;
@end
@implementation wxUIMenuController
- (id) init
{
self = [super init];
return self;
}
/*
#if wxOSX_USE_NEEDSUPDATE_HOOK
- (void)menuNeedsUpdate:(UIMenu*)smenu
{
wxUIMenu* menu = (wxUIMenu*) smenu;
wxMenuImpl* menuimpl = [menu implementation];
if ( menuimpl )
{
wxMenu* wxpeer = (wxMenu*) menuimpl->GetWXPeer();
if ( wxpeer )
wxpeer->HandleMenuOpened();
}
}
#else
- (void)menuWillOpen:(UIMenu *)smenu
{
wxUIMenu* menu = (wxUIMenu*) smenu;
wxMenuImpl* menuimpl = [menu implementation];
if ( menuimpl )
{
wxMenu* wxpeer = (wxMenu*) menuimpl->GetWXPeer();
if ( wxpeer )
wxpeer->HandleMenuOpened();
}
}
#endif
- (void)menuDidClose:(UIMenu *)smenu
{
wxUIMenu* menu = (wxUIMenu*) smenu;
wxMenuImpl* menuimpl = [menu implementation];
if ( menuimpl )
{
wxMenu* wxpeer = (wxMenu*) menuimpl->GetWXPeer();
if ( wxpeer )
wxpeer->HandleMenuClosed();
}
}
- (void)menu:(UIMenu *)smenu willHighlightItem:(UIMenuItem *)item
{
wxUIMenu* menu = (wxUIMenu*) smenu;
wxMenuImpl* menuimpl = [menu implementation];
if ( menuimpl )
{
wxMenuItem* menuitem = nullptr;
wxMenu* wxpeer = (wxMenu*) menuimpl->GetWXPeer();
if ( [ item isKindOfClass:[wxUIMenuItem class] ] )
{
wxMenuItemImpl* menuitemimpl = (wxMenuItemImpl*) [ (wxUIMenuItem*) item implementation ];
if ( menuitemimpl )
{
menuitem = menuitemimpl->GetWXPeer();
}
}
if ( wxpeer )
{
wxpeer->HandleMenuItemHighlighted( menuitem );
}
}
}
*/
@end
class wxMenuCocoaImpl : public wxMenuImpl
class wxMenuIPhoneImpl : public wxMenuImpl
{
public :
wxMenuCocoaImpl( wxMenu* peer , UIMenu* menu) : wxMenuImpl(peer), m_osxMenu(wxCFRetain(menu))
wxMenuIPhoneImpl( wxMenu* peer , UIMenu* menu) : wxMenuImpl(peer), m_osxMenu(wxCFRetain(menu))
{
/*
static wxUIMenuController* controller = nullptr;
if ( controller == nullptr )
{
controller = [[wxUIMenuController alloc] init];
}
[menu setDelegate:controller];
[m_osxMenu setImplementation:this];
// gc aware
if ( m_osxMenu )
CFRetain(m_osxMenu);
[m_osxMenu release];
*/
}
virtual ~wxMenuCocoaImpl();
virtual ~wxMenuIPhoneImpl();
virtual void InsertOrAppend(wxMenuItem *pItem, size_t pos) override
{
UIMenuElement* uimenuitem = (UIMenuElement*) pItem->GetPeer()->GetHMenuItem();
m_children.push_back(uimenuitem);
/*
// make sure a call of SetSubMenu is also reflected (occurring after Create)
// update the native menu item accordingly
if ( pItem->IsSubMenu() )
{
wxMenu* wxsubmenu = pItem->GetSubMenu();
WXHMENU nssubmenu = wxsubmenu->GetHMenu();
if ( [nsmenuitem submenu] != nssubmenu )
{
wxsubmenu->GetPeer()->SetTitle( pItem->GetItemLabelText() );
[nsmenuitem setSubmenu:nssubmenu];
}
}
if ( pos == (size_t) -1 )
[m_osxMenu addItem:nsmenuitem ];
else
[m_osxMenu insertItem:nsmenuitem atIndex:pos];
*/
}
virtual void Remove( wxMenuItem *pItem ) override
{
// [m_osxMenu removeItem:(UIMenuItem*) pItem->GetPeer()->GetHMenuItem()];
}
virtual void MakeRoot() override
@@ -214,8 +64,6 @@ public :
virtual void SetTitle( const wxString& text ) override
{
// wxCFStringRef cfText(text);
// [m_osxMenu setTitle:cfText.AsNSString()];
}
virtual void PopUp( wxWindow *win, int x, int y ) override
@@ -271,29 +119,38 @@ public :
void DisableAutoEnable()
{
/*
[m_osxMenu setAutoenablesItems:NO];
wxMenu* menu = GetWXPeer();
for ( wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
node;
node = node->GetNext() )
{
const wxMenuItem* const item = node->GetData();
if ( item->IsSubMenu() )
{
wxMenuCocoaImpl* subimpl = dynamic_cast<wxMenuCocoaImpl*>(item->GetSubMenu()->GetPeer());
if ( subimpl )
subimpl->DisableAutoEnable();
}
}
*/
}
WXHMENU GetHMenu() override {
if ( m_osxMenu == nil )
{
m_osxMenu.reset( [UIMenu menuWithTitle:wxCFStringRef(m_peer->GetTitle()).AsNSString() children:m_children] );
NSString* title = wxNSStringWithWxString(wxStripMenuCodes(m_peer->GetTitle(), wxStrip_Menu));
wxCFMutableArrayRef<UIMenu*> groups;
wxCFMutableArrayRef<UIMenuElement*> currentMenuElements;
for ( int i = 0 ; i < m_children.size(); i++ )
{
UIMenuElement* element = m_children[i];
if ( element.title.length > 0 )
{
currentMenuElements.push_back(element);
}
else // separator, start a new menu group
{
UIMenu* menu = [UIMenu menuWithTitle:@"" image:nil identifier:nil options:UIMenuOptionsDisplayInline children:currentMenuElements];
currentMenuElements.clear();
groups.push_back(menu);
}
}
if ( !currentMenuElements.empty() )
{
UIMenu* menu = [UIMenu menuWithTitle:@"" image:nil identifier:nil options:UIMenuOptionsDisplayInline children:currentMenuElements];
currentMenuElements.clear();
groups.push_back(menu);
}
m_osxMenu.reset( [UIMenu menuWithTitle:title children:groups] );
}
return m_osxMenu;
}
@@ -303,25 +160,70 @@ public :
protected :
wxCFRef<UIMenu*> m_osxMenu;
wxCFMutableArrayRef<UIMenuElement*> m_children;
/*
UIMenu* menu = [[UIMenu menuWithTitle:cfText.AsNSString() children:];
*/
} ;
wxMenuCocoaImpl::~wxMenuCocoaImpl()
wxMenuIPhoneImpl::~wxMenuIPhoneImpl()
{
// [m_osxMenu setDelegate:nil];
// [m_osxMenu setImplementation:nil];
// gc aware
}
wxMenuImpl* wxMenuImpl::Create( wxMenu* peer, const wxString& title )
{
wxCFStringRef cfText( title );
wxMenuImpl* c = new wxMenuCocoaImpl( peer, nil );
wxMenuImpl* c = new wxMenuIPhoneImpl( peer, nil );
return c;
}
#if wxUSE_MENUBAR
void wxMenuBar::OSXOnBuildMenu(WX_NSObject b)
{
id<UIMenuBuilder> builder = (id<UIMenuBuilder>) b;
UIMenuIdentifier lastmenuid = UIMenuApplication;
[builder removeMenuForIdentifier:UIMenuFile];
[builder removeMenuForIdentifier:UIMenuEdit];
[builder removeMenuForIdentifier:UIMenuFormat];
[builder removeMenuForIdentifier:UIMenuView];
NSString* nsHelpMenuTitle = wxNSStringWithWxString(wxStripMenuCodes(wxApp::s_macHelpMenuTitleName, wxStrip_Menu));
NSString* nsTranslatedHelpTitle = wxNSStringWithWxString(wxGETTEXT_IN_CONTEXT("macOS menu name", "Help"));
NSString* nsWindowMenuTitle = wxNSStringWithWxString(wxStripMenuCodes(wxApp::s_macWindowMenuTitleName, wxStrip_Menu));
NSString* nsTranslatedWindowMenuTitle = wxNSStringWithWxString(wxGETTEXT_IN_CONTEXT("macOS menu name", "Window"));
for ( wxMenuItemList::compatibility_iterator node = m_rootMenu->GetMenuItems().GetFirst();
node;
node = node->GetNext() )
{
const wxMenuItem* const item = node->GetData();
if ( item->IsSubMenu() )
{
UIMenu* menu = (UIMenu *)item->GetSubMenu()->GetHMenu();
if ( item->GetSubMenu() == m_appleMenu )
{
// the default is quite resonable
// TODO merge non desktop commands into the application menu, discard the rest
}
else if ([[menu title] isEqualToString:nsWindowMenuTitle] ||
[[menu title] isEqualToString:nsTranslatedWindowMenuTitle])
{
[builder replaceMenuForIdentifier:UIMenuWindow withMenu:menu];
}
else if ([[menu title] isEqualToString:nsHelpMenuTitle] ||
[[menu title] isEqualToString:nsTranslatedHelpTitle])
{
[builder replaceMenuForIdentifier:UIMenuHelp withMenu:menu];
}
else
{
[builder insertSiblingMenu:menu afterMenuForIdentifier:(UIMenuIdentifier) lastmenuid];
}
lastmenuid = menu.identifier;
}
}
}
#endif
#endif
+12 -9
View File
@@ -182,14 +182,14 @@ void wxMacCocoaMenuItemSetAccelerator( UIMenuItem* menuItem, wxAcceleratorEntry*
*/
class wxMenuItemCocoaImpl : public wxMenuItemImpl
class wxMenuItemIPhoneImpl : public wxMenuItemImpl
{
public :
wxMenuItemCocoaImpl( wxMenuItem* peer, UIMenuElement* item ) : wxMenuItemImpl(peer), m_osxMenuItem(wxCFRetain(item))
wxMenuItemIPhoneImpl( wxMenuItem* peer, UIMenuElement* item ) : wxMenuItemImpl(peer), m_osxMenuItem(wxCFRetain(item))
{
}
~wxMenuItemCocoaImpl();
~wxMenuItemIPhoneImpl();
void SetBitmap( const wxBitmapBundle& bitmap ) override
{
@@ -244,11 +244,11 @@ protected :
wxCFRef<UIMenuElement*> m_osxMenuItem ;
} ;
wxMenuItemCocoaImpl::~wxMenuItemCocoaImpl()
wxMenuItemIPhoneImpl::~wxMenuItemIPhoneImpl()
{
}
bool wxMenuItemCocoaImpl::DoDefault()
bool wxMenuItemIPhoneImpl::DoDefault()
{
bool handled=false;
int menuid = m_peer->GetId();
@@ -291,7 +291,8 @@ wxMenuItemImpl* wxMenuItemImpl::Create( wxMenuItem* peer, wxMenu *pParentMenu,
if ( kind == wxITEM_SEPARATOR )
{
// TODO new version of item = [[UIMenuItem separatorItem] retain];
UIAction* menuItem = [UIAction actionWithTitle:@"" image:nil identifier:nil handler: ^( UIAction* action) {} ];
item = menuItem;
}
else
{
@@ -305,12 +306,14 @@ wxMenuItemImpl* wxMenuItemImpl::Create( wxMenuItem* peer, wxMenu *pParentMenu,
}
else
{
wxString identifier = wxString::Format( wxT("%p"), peer );
wxCFStringRef cfidentifier(identifier);
// currently in the iOS 14 Beta UICommands trigger a layout violation so stick with UIActions for the time being
if ( entry )
{
// TODO UIKeyCommand* command = [UIKeyCommand commandWithTitle ...]
UIAction* menuitem = [UIAction actionWithTitle:cfText.AsNSString() image:nil identifier:nil
UIAction* menuitem = [UIAction actionWithTitle:cfText.AsNSString() image:nil identifier:cfidentifier.AsNSString()
handler: ^( UIAction* action) { peer->GetMenu()->HandleCommandProcess(peer);} ];
item = menuitem;
}
@@ -320,13 +323,13 @@ wxMenuItemImpl* wxMenuItemImpl::Create( wxMenuItem* peer, wxMenu *pParentMenu,
UICommand* command = [UICommand commandWithTitle:cfText.AsNSString() image:nil action:@selector(menuItemAction:) propertyList:nil];
*/
UIAction* menuitem = [UIAction actionWithTitle:cfText.AsNSString() image:nil identifier:nil
UIAction* menuitem = [UIAction actionWithTitle:cfText.AsNSString() image:nil identifier:cfidentifier.AsNSString()
handler: ^( UIAction* action) { peer->GetMenu()->HandleCommandProcess(peer);} ];
item = menuitem;
}
}
}
c = new wxMenuItemCocoaImpl( peer, item );
c = new wxMenuItemIPhoneImpl( peer, item );
return c;
}
+16 -4
View File
@@ -42,7 +42,7 @@
// Common Event Support
// ----------------------------------------------------------------------------
@interface wxAppDelegate : NSObject <UIApplicationDelegate> {
@interface wxAppDelegate : UIResponder <UIApplicationDelegate> {
}
@end
@@ -57,19 +57,31 @@
return YES;
}
- (void)applicationDidFinishLaunching:(UIApplication *)application {
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
wxTheApp->OSXOnDidFinishLaunching();
}
- (void)applicationWillTerminate:(UIApplication *)application {
- (void)applicationWillTerminate:(UIApplication *)application
{
wxUnusedVar(application);
wxTheApp->OSXOnWillTerminate();
}
- (void)dealloc {
- (void)dealloc
{
[super dealloc];
}
#if wxUSE_MENUBAR
- (void) buildMenuWithBuilder:(id<UIMenuBuilder>) builder
{
if ( builder.system == UIMenuSystem.mainSystem )
{
wxTheApp->OSXOnBuildMenu((WX_NSObject) builder);
}
}
#endif
@end
+6 -2
View File
@@ -665,11 +665,13 @@ wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
{
// must be set early because base class method call might call back and create a native menu which would be missing a title
menu->SetTitle(title);
if ( !wxMenuBarBase::Insert(pos, menu, title) )
return false;
m_rootMenu->Insert( pos+firstMenuPos, wxMenuItem::New( m_rootMenu, wxID_ANY, title, "", wxITEM_NORMAL, menu ) );
menu->SetTitle(title);
return true;
}
@@ -688,6 +690,9 @@ wxMenu *wxMenuBar::Remove(size_t pos)
bool wxMenuBar::Append(wxMenu *menu, const wxString& title)
{
// must be set early because base class method call might call back and create a native menu which would be missing a title
menu->SetTitle(title);
WXHMENU submenu = menu ? menu->GetHMenu() : nullptr;
wxCHECK_MSG( submenu, false, wxT("can't append invalid menu to menubar") );
@@ -695,7 +700,6 @@ bool wxMenuBar::Append(wxMenu *menu, const wxString& title)
return false;
m_rootMenu->AppendSubMenu(menu, title);
menu->SetTitle(title);
return true;
}