Merge branch 'accel-key-event'

Allow customizing priority of accelerators compared to normal key
events.

See #27106.

Closes #22625, #22630.
This commit is contained in:
Vadim Zeitlin
2026-09-28 16:36:27 +02:00
53 changed files with 1390 additions and 224 deletions
+3
View File
@@ -91,6 +91,9 @@ public:
return m_keyCode != 0;
}
// Return true if the given key event corresponds to this accelerator.
bool MatchesEvent(const wxKeyEvent& event) const;
// string <-> wxAcceleratorEntry conversion
// ----------------------------------------
+60
View File
@@ -659,6 +659,7 @@ class WXDLLIMPEXP_FWD_CORE wxMouseEvent;
class WXDLLIMPEXP_FWD_CORE wxFocusEvent;
class WXDLLIMPEXP_FWD_CORE wxChildFocusEvent;
class WXDLLIMPEXP_FWD_CORE wxKeyEvent;
class WXDLLIMPEXP_FWD_CORE wxAcceleratorKeyEvent;
class WXDLLIMPEXP_FWD_CORE wxNavigationKeyEvent;
class WXDLLIMPEXP_FWD_CORE wxSetCursorEvent;
class WXDLLIMPEXP_FWD_CORE wxScrollEvent;
@@ -761,6 +762,7 @@ wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_CHAR_HOOK, wxKeyEvent);
wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_NAVIGATION_KEY, wxNavigationKeyEvent);
wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_KEY_DOWN, wxKeyEvent);
wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_KEY_UP, wxKeyEvent);
wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_ACCELERATOR_KEY, wxAcceleratorKeyEvent);
#if wxUSE_HOTKEY
wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_HOTKEY, wxKeyEvent);
#endif
@@ -2456,6 +2458,60 @@ private:
wxDECLARE_DYNAMIC_CLASS(wxKeyEvent);
};
// Accelerator key event class
/*
wxEVT_ACCELERATOR_KEY
*/
// Handle this event to change the default logic for determining whether a key
// should be processed as an accelerator or not: simply handling it prevents
// the key from being used as an accelerator, while handling it and calling
// UseAccelerator() forces using it as an accelerator, overriding the window.
class WXDLLIMPEXP_CORE wxAcceleratorKeyEvent : public wxKeyEvent
{
public:
wxAcceleratorKeyEvent() = default;
wxAcceleratorKeyEvent(const wxKeyEvent& event,
int command,
wxMenuItem* menuItem = nullptr)
: wxKeyEvent(wxEVT_ACCELERATOR_KEY, event),
m_command(command),
m_menuItem(menuItem)
{
// This event is similar to wxEVT_CHAR_HOOK in that it is sent to the
// focused window but can be handled by any of its parents.
m_propagationLevel = wxEVENT_PROPAGATE_MAX;
}
// Get the ID of the command which would be generated by the accelerator.
int GetCommand() const { return m_command; }
// Get the menu item corresponding to the accelerator, may be null if the
// accelerator doesn't come from a menu item but from wxAcceleratorTable.
wxMenuItem* GetMenuItem() const { return m_menuItem; }
// Explicitly request using the accelerator: this is useful for overriding
// wxWindow::ClaimsKeyBeforeAccelerator() decision.
void UseAccelerator() { m_useAccel = true; }
// Implementation only from now on.
bool ShouldUseAccelerator() const { return m_useAccel; }
wxNODISCARD virtual wxEvent *Clone() const override
{ return new wxAcceleratorKeyEvent(*this); }
private:
int m_command = wxID_NONE;
wxMenuItem* m_menuItem = nullptr;
bool m_useAccel = false;
wxDECLARE_DYNAMIC_CLASS(wxAcceleratorKeyEvent);
};
// Size event class
/*
wxEVT_SIZE
@@ -4370,6 +4426,7 @@ typedef void (wxEvtHandler::*wxNcPaintEventFunction)(wxNcPaintEvent&);
typedef void (wxEvtHandler::*wxEraseEventFunction)(wxEraseEvent&);
typedef void (wxEvtHandler::*wxMouseEventFunction)(wxMouseEvent&);
typedef void (wxEvtHandler::*wxCharEventFunction)(wxKeyEvent&);
typedef void (wxEvtHandler::*wxAcceleratorKeyEventFunction)(wxAcceleratorKeyEvent&);
typedef void (wxEvtHandler::*wxFocusEventFunction)(wxFocusEvent&);
typedef void (wxEvtHandler::*wxChildFocusEventFunction)(wxChildFocusEvent&);
typedef void (wxEvtHandler::*wxActivateEventFunction)(wxActivateEvent&);
@@ -4429,6 +4486,8 @@ typedef void (wxEvtHandler::*wxStylusEventFunction)(wxStylusEvent&);
#define wxCharEventHandler(func) \
wxEVENT_HANDLER_CAST(wxCharEventFunction, func)
#define wxKeyEventHandler(func) wxCharEventHandler(func)
#define wxAcceleratorKeyEventHandler(func) \
wxEVENT_HANDLER_CAST(wxAcceleratorKeyEventFunction, func)
#define wxFocusEventHandler(func) \
wxEVENT_HANDLER_CAST(wxFocusEventFunction, func)
#define wxChildFocusEventHandler(func) \
@@ -4696,6 +4755,7 @@ typedef void (wxEvtHandler::*wxStylusEventFunction)(wxStylusEvent&);
#define EVT_HOTKEY(winid, func) wx__DECLARE_EVT1(wxEVT_HOTKEY, winid, wxCharEventHandler(func))
#endif
#define EVT_CHAR_HOOK(func) wx__DECLARE_EVT0(wxEVT_CHAR_HOOK, wxCharEventHandler(func))
#define EVT_ACCELERATOR_KEY(func) wx__DECLARE_EVT0(wxEVT_ACCELERATOR_KEY, wxAcceleratorKeyEventHandler(func))
#define EVT_MENU_OPEN(func) wx__DECLARE_EVT0(wxEVT_MENU_OPEN, wxMenuEventHandler(func))
#define EVT_MENU_CLOSE(func) wx__DECLARE_EVT0(wxEVT_MENU_CLOSE, wxMenuEventHandler(func))
#define EVT_MENU_HIGHLIGHT(winid, func) wx__DECLARE_EVT1(wxEVT_MENU_HIGHLIGHT, winid, wxMenuEventHandler(func))
+7
View File
@@ -212,6 +212,13 @@ protected:
// override to do something special when the menu bar is attached to the
// frame
virtual void AttachMenuBar(wxMenuBar *menubar);
#if wxUSE_ACCEL
// search for an accelerator for the given key event in our menus
virtual bool
FindAcceleratorForKeyInMenuBar(const wxKeyEvent& event,
wxAcceleratorEntry& entry) const override;
#endif // wxUSE_ACCEL
#endif // wxUSE_MENUBAR
// Return true if we should update the menu item state from idle event
+6
View File
@@ -131,6 +131,12 @@ public:
virtual const wxTextEntry* WXGetTextEntry() const override { return this; }
protected:
#if wxUSE_ACCEL
// Reserve the keys used for editing the text in this control.
virtual bool ClaimsKeyBeforeAccelerator(const wxKeyEvent& event,
int command) const override;
#endif // wxUSE_ACCEL
// From wxWindowGTK:
virtual GdkWindow *GTKGetWindow(wxArrayGdkWindows& windows) const override;
+16
View File
@@ -188,6 +188,22 @@ public:
// base version just calls HandleWindowEvent()
virtual bool GTKProcessEvent(wxEvent& event) const;
#if wxUSE_ACCEL
// Called when processing key presses to determine whether they should
// trigger an accelerator or be processed normally in the window itself.
enum class AcceleratorVerdict
{
Nothing, // No accelerator defined for this key event.
Menu, // Accelerator defined for a menu item.
Table, // Accelerator defined in the wxAcceleratorTable.
Window // Window claims the key event and will process it itself.
};
AcceleratorVerdict
GTKShouldUseAccelerator(const wxKeyEvent& event,
wxWindowGTK** accelOwner,
int* command) const;
#endif // wxUSE_ACCEL
// Map GTK widget direction of the given widget to/from wxLayoutDirection
static wxLayoutDirection GTKGetLayout(GtkWidget *widget);
static void GTKSetLayout(GtkWidget *widget, wxLayoutDirection dir);
+10
View File
@@ -226,6 +226,11 @@ public:
// find by position
wxMenuItem* FindItemByPosition(size_t position) const;
#if wxUSE_ACCEL
// find the item using the accelerator corresponding to the given key if any
wxMenuItem* FindItemForAccelKey(const wxKeyEvent& event) const;
#endif // wxUSE_ACCEL
// get/set items attributes
void Enable(int itemid, bool enable);
bool IsEnabled(int itemid) const;
@@ -510,6 +515,11 @@ public:
// find menu by its caption, return wxNOT_FOUND on failure
int FindMenu(const wxString& title) const;
#if wxUSE_ACCEL
// find the item using the accelerator in all our menus
wxMenuItem* FindItemForAccelKey(const wxKeyEvent& event) const;
#endif // wxUSE_ACCEL
// item access
// -----------
+7
View File
@@ -36,6 +36,13 @@ public:
// translate the accelerator, return true if done
bool Translate(wxWindow *window, WXMSG *msg) const;
// Find the entry corresponding to the given key event or nullptr.
//
// Note that the entries are reconstructed from the native accelerator
// table when necessary, so this function may be relatively slow when it's
// called for the first time.
const wxAcceleratorEntry *GetEntry(const wxKeyEvent& event) const;
private:
wxDECLARE_DYNAMIC_CLASS(wxAcceleratorTable);
};
+6 -1
View File
@@ -99,7 +99,12 @@ public:
virtual bool MSWCommand(WXUINT param, WXWORD id) override;
bool MSWProcessEditMsg(WXUINT msg, WXWPARAM wParam, WXLPARAM lParam);
virtual WXLRESULT MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) override;
bool MSWShouldPreProcessMessage(WXMSG *pMsg) override;
#if wxUSE_ACCEL
// Reserve the keys used for editing the text in this control.
virtual bool ClaimsKeyBeforeAccelerator(const wxKeyEvent& event,
int command) const override;
#endif // wxUSE_ACCEL
// Standard event handling
void OnCut(wxCommandEvent& event);
+5 -3
View File
@@ -139,9 +139,11 @@ protected:
void OnSetFocus(wxFocusEvent& event);
void OnKillFocus(wxFocusEvent& event);
// returns true for special keys like "Ctrl+C" that should be handled
// by the text control
virtual bool MSWShouldPreProcessMessage(WXMSG* msg) override;
#if wxUSE_ACCEL
// Reserve the keys used for editing the text in the buddy control.
virtual bool ClaimsKeyBeforeAccelerator(const wxKeyEvent& event,
int command) const override;
#endif // wxUSE_ACCEL
// generate spin control update event with the given value
void SendSpinUpdate(int value);
-4
View File
@@ -84,10 +84,6 @@ protected:
bool MSWUsesStandardAutoComplete() const { return false; }
#endif //wxUSE_OLE
// Returns false if this message shouldn't be preprocessed, but is always
// handled by the EDIT control represented by this object itself.
bool MSWShouldPreProcessMessage(WXMSG* msg) const;
// Helper for wxTE_PROCESS_ENTER handling: activates the default button in
// the dialog containing this control if any.
bool ClickDefaultButtonIfPossible();
+5
View File
@@ -439,6 +439,11 @@ public:
// dispatched normally
virtual bool MSWShouldPreProcessMessage(WXMSG* pMsg);
// return false if the accelerators shouldn't be used for this message
// because this window prefers to handle this key itself, this is
// determined by sending wxEVT_ACCELERATOR_KEY event
bool MSWShouldUseAcceleratorForKey(const WXMSG* pMsg);
// return true if the message was preprocessed and shouldn't be dispatched
virtual bool MSWProcessMessage(WXMSG* pMsg);
+3
View File
@@ -24,6 +24,9 @@ public:
bool IsOk() const;
int GetCommand( wxKeyEvent &event );
// Find the entry corresponding to the given key event or nullptr.
const wxAcceleratorEntry *GetEntry(const wxKeyEvent& event) const;
};
#endif
+12
View File
@@ -324,6 +324,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; }
+3
View File
@@ -50,6 +50,9 @@ public:
bool Ok() const { return IsOk(); }
bool IsOk() const;
// Find the entry corresponding to the given key event or nullptr.
const wxAcceleratorEntry *GetEntry(const wxKeyEvent& event) const;
protected:
// ref counting code
virtual wxObjectRefData *CreateRefData() const override;
+5
View File
@@ -85,6 +85,11 @@ public:
virtual bool QtHandleFocusEvent(QWidget *handler, QFocusEvent *event) override;
protected:
#if wxUSE_ACCEL
// Reserve the keys used for editing the text in this control.
virtual bool ClaimsKeyBeforeAccelerator(const wxKeyEvent& event,
int command) const override;
#endif // wxUSE_ACCEL
// From wxTextEntry:
virtual wxString DoGetValue() const override;
+39
View File
@@ -12,6 +12,7 @@
#include <QtCore/QEvent>
#include <QtGui/QCloseEvent>
#include <QtGui/QKeyEvent>
#include "wx/log.h"
#include "wx/window.h"
@@ -366,6 +367,44 @@ protected:
{
switch (event->type())
{
#if wxUSE_ACCEL
case QEvent::ShortcutOverride:
// Qt sends this event before using the key for a shortcut,
// which allows us to decide whether it should really be used.
//
// Note that this event is also sent to all the parents of the
// focused widget, but we only need to handle it once, in the
// window which is going to get the key event.
if ( this->hasFocus() )
{
if ( wxWindow* const handler = this->GetHandler() )
{
auto* const keyEvent = static_cast<QKeyEvent*>(event);
switch ( handler->QtShouldUseAccelerator(keyEvent) )
{
case wxWindow::AcceleratorVerdict::Nothing:
// There is no accelerator defined anyhow.
break;
case wxWindow::AcceleratorVerdict::Accel:
// Allow the shortcut to be used and prevent
// the widget itself from claiming this key, as
// e.g. QLineEdit does for Ctrl-C.
event->ignore();
return true;
case wxWindow::AcceleratorVerdict::Window:
// Accepting means that we will get the key
// events (and the shortcut won't be used).
event->accept();
return true;
}
}
}
break;
#endif // wxUSE_ACCEL
case QEvent::Gesture:
return gestureEvent(static_cast<QGestureEvent*>(event), event);
+13 -1
View File
@@ -179,8 +179,21 @@ public:
static wxWindowQt *QtRetrieveWindowPointer( const QWidget *widget );
static void QtSendSetCursorEvent(wxWindowQt* win, const wxPoint& posClient);
// Fill in the given wx key event from the Qt one.
void QtFillKeyEvent ( wxKeyEvent& e, const QKeyEvent *event ) const;
#if wxUSE_ACCEL
virtual void QtHandleShortcut ( int command );
// Called when Qt is about to use a shortcut for the given key to decide
// what should really happen with it.
enum class AcceleratorVerdict
{
Nothing, // No accelerator defined for this key event.
Accel, // The accelerator should be used.
Window // Window claims the key event and will process it itself.
};
AcceleratorVerdict QtShouldUseAccelerator ( QKeyEvent *event );
#endif // wxUSE_ACCEL
#if wxUSE_TOOLTIPS
@@ -264,7 +277,6 @@ private:
#if wxUSE_ACCEL
wxVector<QShortcut*> m_qtShortcuts; // owned by whatever GetHandle() returns
std::unique_ptr<wxQtShortcutHandler> m_qtShortcutHandler; // always allocated
bool m_processingShortcut;
#endif // wxUSE_ACCEL
wxDECLARE_DYNAMIC_CLASS_NO_COPY( wxWindowQt );
+8
View File
@@ -56,6 +56,14 @@ public:
// Select text in the textctrl
virtual void SetSelection(long from, long to) = 0;
protected:
#if wxUSE_ACCEL
// Reserve the keys used for editing the text in this control, as the
// controls implemented in terms of wxTextCtrl already do.
virtual bool ClaimsKeyBeforeAccelerator(const wxKeyEvent& event,
int command) const override;
#endif // wxUSE_ACCEL
private:
wxDECLARE_NO_COPY_CLASS(wxSpinCtrlBase);
};
+7
View File
@@ -952,6 +952,13 @@ protected:
// implement the wxTextEntry pure virtual method
virtual wxWindow *GetEditableWindow() override { return this; }
#if wxUSE_ACCEL
// Reserve the keys used for editing the text for this control instead of
// letting the accelerators using them be triggered.
virtual bool ClaimsKeyBeforeAccelerator(const wxKeyEvent& event,
int command) const override;
#endif // wxUSE_ACCEL
wxDECLARE_NO_COPY_CLASS(wxTextCtrlBase);
wxDECLARE_ABSTRACT_CLASS(wxTextCtrlBase);
};
+6
View File
@@ -14,6 +14,7 @@
#include "wx/gdicmn.h" // for wxPoint
class WXDLLIMPEXP_FWD_BASE wxArrayString;
class WXDLLIMPEXP_FWD_CORE wxKeyEvent;
class WXDLLIMPEXP_FWD_CORE wxTextCompleter;
class WXDLLIMPEXP_FWD_CORE wxTextEntryHintData;
class WXDLLIMPEXP_FWD_CORE wxWindow;
@@ -184,6 +185,11 @@ public:
// NB: this is public for wxRichTextCtrl use only right now, do not call it
static bool SendTextUpdatedEvent(wxWindow *win);
// Return true if the given event carries one of the keys used for editing
// the text and so should be handled by the text entry itself rather than
// being used as an accelerator, even if one is defined for it.
static bool IsUsedForEditing(const wxKeyEvent& event);
// generate the wxEVT_TEXT event for this window
bool SendTextUpdatedEvent()
{
+57 -1
View File
@@ -962,7 +962,8 @@ public:
{ m_acceleratorTable = accel; }
wxAcceleratorTable *GetAcceleratorTable()
{ return &m_acceleratorTable; }
const wxAcceleratorTable* GetAcceleratorTable() const
{ return &m_acceleratorTable; }
#endif // wxUSE_ACCEL
#if wxUSE_HOTKEY
@@ -1809,6 +1810,61 @@ protected:
// implementation of Navigate() and NavigateIn()
virtual bool DoNavigateIn(int flags);
#if wxUSE_ACCEL
// Return true if this window wants the key to be used for its own needs,
// even if it's registered as an accelerator for the given command.
//
// By default returns false meaning that accelerators are used if nothing
// special is done.
//
// Note that this function is not called at all if the application handles
// wxEVT_ACCELERATOR_KEY, it only defines the default behaviour.
//
// This function may be overridden in user-defined classes.
virtual bool
ClaimsKeyBeforeAccelerator(const wxKeyEvent& WXUNUSED(event),
int WXUNUSED(command)) const
{
return false;
}
// Find the accelerator corresponding to the given key in this window or
// any of its (grand)parents and searching both the accelerator tables and
// the accelerators used by the menu bar menus items.
//
// If the function returns true, "entry" is filled with the found
// accelerator, otherwise it's left unchanged.
//
// "owner" may be null but if it isn't, it is filled with the window owning
// the accelerator table or the menu bar containing the accelerator.
//
// This function is only used by wx internally.
bool
FindAcceleratorForKey(const wxKeyEvent& event,
wxAcceleratorEntry& entry,
wxWindow** owner) const;
// Find the accelerator corresponding to the given key in the menu bar of
// this window.
//
// This is used by FindAcceleratorForKey() and simply returns false, it's
// overridden in wxFrame to really search the menu bar.
virtual bool
FindAcceleratorForKeyInMenuBar(const wxKeyEvent& event,
wxAcceleratorEntry& entry) const;
// Return true if the given key event should be processed as an accelerator
// or false if it should be handled as a normal key press.
//
// Sends wxEVT_ACCELERATOR_KEY and calls ClaimsKeyBeforeAccelerator() to
// decide what to do.
//
// This function is only used by wx internally.
bool ShouldUseAcceleratorForKey(const wxKeyEvent& event,
int command,
wxMenuItem* menuItem) const;
#endif // wxUSE_ACCEL
#if wxUSE_CONSTRAINTS
// satisfy the constraints for the windows but don't set the window sizes
void SatisfyConstraints();