Implement configurable accelerators support for wxOSX

Use the new API, i.e. call ClaimsKeyBeforeAccelerator() and send
wxEVT_ACCELERATOR_KEY, in wxOSX too, whenever possible, i.e. not for
Cmd-Letter used by the menu items as we never get key events for them at
all.

This probably could be worked around by overriding performKeyEquivalent:
but for now leave it like this because hopefully there should be no
conflict between menu accelerators and standard text editing keys as
Cmd+C etc act on the focused window anyhow by default, so the behaviour
should be already correct anyhow.
This commit is contained in:
Vadim Zeitlin
2026-09-26 23:57:58 +02:00
parent 1043551376
commit 5ca0f8aa73
4 changed files with 79 additions and 13 deletions
+12
View File
@@ -319,6 +319,18 @@ public:
virtual bool OSXHandleClicked( double timestampsec );
virtual bool OSXHandleKeyEvent( wxKeyEvent& event );
#if wxUSE_ACCEL
// Return false if the menu accelerator using the key corresponding to the
// given event shouldn't be used because this window needs this key for
// its own purposes.
//
// Note that this function only deals with the accelerators of the menu
// items, the ones defined by wxAcceleratorTable are checked later, from
// OSXHandleKeyEvent() itself.
bool OSXShouldUseMenuAcceleratorForKey( const wxKeyEvent& event );
#endif // wxUSE_ACCEL
virtual void OSXSimulateFocusEvents();
bool IsNativeWindowWrapper() const { return m_isNativeWindowWrapper; }
+8
View File
@@ -1780,6 +1780,14 @@ public:
the parents of this window, up to the top level one, which allows to handle
it in the frame containing the control having the focus.
@note Under macOS this event is sent before `wxEVT_CHAR_HOOK` and not after
it. It is also not generated at all for the key combinations
involving the Command key used by the menu items because such keys are
handled by the system before the application has any chance to
intercept them. This is not usually a problem because the standard
macOS shortcuts such as `Cmd-C` are implemented by the menu items
acting on the currently focused control anyhow.
@beginEventTable{wxAcceleratorKeyEvent}
@event{EVT_ACCELERATOR_KEY(func)}
Process a `wxEVT_ACCELERATOR_KEY` event.
+24 -4
View File
@@ -1706,11 +1706,31 @@ void wxWidgetCocoaImpl::keyEvent(WX_NSEvent event, WXWidget slf, void *_cmd)
{
// there are key equivalents that are not command-combos and therefore not handled by cocoa automatically,
// therefore we call the menubar directly here, exit if the menu is handling the shortcut
if ( [[[NSApplication sharedApplication] mainMenu] performKeyEquivalent:event] )
#if wxUSE_ACCEL
// The focused window may want to handle this key itself instead of
// letting the menu use it as an accelerator, so ask it first.
bool useMenuAccel = true;
if ( wxWindowMac* const peer = GetWXPeer() )
{
wxLogTrace(TRACE_KEYS, "%s processed as key equivalent by the menu",
wxDumpSelector((SEL)_cmd));
return;
wxKeyEvent wxevent(wxEVT_KEY_DOWN);
SetupKeyEvent(wxevent, event);
// SetupKeyEvent() may change the event type, so check that this
// is really a key press.
if ( wxevent.GetEventType() == wxEVT_KEY_DOWN )
useMenuAccel = peer->OSXShouldUseMenuAcceleratorForKey(wxevent);
}
if ( useMenuAccel )
#endif // wxUSE_ACCEL
{
if ( [[[NSApplication sharedApplication] mainMenu] performKeyEquivalent:event] )
{
wxLogTrace(TRACE_KEYS, "%s processed as key equivalent by the menu",
wxDumpSelector((SEL)_cmd));
return;
}
}
BeginNativeKeyDownEvent(event);
+35 -9
View File
@@ -2616,11 +2616,16 @@ bool wxWindowMac::OSXHandleKeyEvent( wxKeyEvent& event )
#if wxUSE_ACCEL
if (event.GetEventType() == wxEVT_CHAR_HOOK)
{
wxWindow *ancestor = this;
while (ancestor)
// Check if this key is used by one of the accelerators defined by
// wxAcceleratorTable: note that the accelerators of the menu items
// are not handled here, they are used by the menu bar itself, see
// OSXShouldUseMenuAcceleratorForKey().
wxAcceleratorEntry entry;
wxWindow* ancestor = nullptr;
if ( FindAcceleratorForKey(event, entry, &ancestor) && !entry.GetMenuItem() )
{
int command = ancestor->GetAcceleratorTable()->GetCommand( event );
if (command != -1)
const int command = entry.GetCommand();
if ( ShouldUseAcceleratorForKey(event, command, nullptr) )
{
wxEvtHandler * const handler = ancestor->GetEventHandler();
@@ -2636,11 +2641,6 @@ bool wxWindowMac::OSXHandleKeyEvent( wxKeyEvent& event )
// accelerator.
return true;
}
if (ancestor->IsTopNavigationDomain(wxWindow::Navigation_Accel))
break;
ancestor = ancestor->GetParent();
}
}
#endif // wxUSE_ACCEL
@@ -2648,6 +2648,32 @@ bool wxWindowMac::OSXHandleKeyEvent( wxKeyEvent& event )
return false;
}
#if wxUSE_ACCEL
bool wxWindowMac::OSXShouldUseMenuAcceleratorForKey( const wxKeyEvent& event )
{
wxAcceleratorEntry entry;
if ( !FindAcceleratorForKey(event, entry, nullptr /* don't need owner */) )
{
// This key is not used by any accelerator at all.
return true;
}
wxMenuItem* const item = entry.GetMenuItem();
if ( !item )
{
// This one comes from wxAcceleratorTable and will be dealt with in
// OSXHandleKeyEvent() when we get wxEVT_CHAR_HOOK for this key, don't
// do anything here (and, in particular, don't send the accelerator
// event twice for the same key).
return true;
}
return ShouldUseAcceleratorForKey(event, entry.GetCommand(), item);
}
#endif // wxUSE_ACCEL
wxSize wxWindowMac::GetDPI() const
{
return MakeDPIFromScaleFactor(GetDPIScaleFactor());