Implement configurable accelerators support for wxQt

Use the new API, i.e. call ClaimsKeyBeforeAccelerator() and send
wxEVT_ACCELERATOR_KEY, in wxQt too.

Update wxComboBox to override ClaimsKeyBeforeAccelerator() too.
Note that wxTextCtrl and wxSpinCtrl don't need to be modified because
they already do this in the base class.

This actually simplifies the existing code as we don't need
wxWindow::m_processingShortcut any more.
This commit is contained in:
Vadim Zeitlin
2026-09-26 23:57:58 +02:00
parent b337ad2841
commit e5ac9e8826
5 changed files with 92 additions and 34 deletions
+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);
+10 -1
View File
@@ -184,6 +184,16 @@ public:
#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
@@ -267,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 );
+15
View File
@@ -153,6 +153,21 @@ bool wxComboBox::Create(wxWindow *parent, wxWindowID id,
return wxChoiceBase::Create( parent, id, pos, size, style, validator, name );
}
#if wxUSE_ACCEL
bool
wxComboBox::ClaimsKeyBeforeAccelerator(const wxKeyEvent& event,
int WXUNUSED(command)) const
{
// We don't need any keys if we're not editing any text at all.
if ( IsReadOnly() )
return false;
return IsUsedForEditing(event);
}
#endif // wxUSE_ACCEL
bool wxComboBox::IsReadOnly() const
{
return HasFlag( wxCB_READONLY );
+23 -33
View File
@@ -327,7 +327,6 @@ void wxWindowQt::Init()
#if wxUSE_ACCEL
m_qtShortcutHandler.reset(new wxQtShortcutHandler(this));
m_processingShortcut = false;
#endif
m_qtWindow = nullptr;
m_qtContainer = nullptr;
@@ -1655,6 +1654,29 @@ void wxWindowQt::QtFillKeyEvent( wxKeyEvent& e, const QKeyEvent *event ) const
wxQtFillKeyboardModifiers( event->modifiers(), &e );
}
#if wxUSE_ACCEL
wxWindowQt::AcceleratorVerdict
wxWindowQt::QtShouldUseAccelerator ( QKeyEvent *event )
{
wxKeyEvent e( wxEVT_KEY_DOWN );
QtFillKeyEvent( e, event );
wxAcceleratorEntry entry;
if ( !FindAcceleratorForKey(e, entry, nullptr) )
{
// This key is not used by any accelerator anyhow.
return AcceleratorVerdict::Nothing;
}
if ( ShouldUseAcceleratorForKey(e, entry.GetCommand(), entry.GetMenuItem()) )
return AcceleratorVerdict::Accel;
return AcceleratorVerdict::Window;
}
#endif // wxUSE_ACCEL
bool wxWindowQt::QtHandleKeyEvent ( QWidget *WXUNUSED( handler ), QKeyEvent *event )
{
// qt sends keyup and keydown events for autorepeat, but this is not
@@ -1663,18 +1685,6 @@ bool wxWindowQt::QtHandleKeyEvent ( QWidget *WXUNUSED( handler ), QKeyEvent *eve
if ( event->isAutoRepeat() && event->type() == QEvent::KeyRelease )
return true;
#if wxUSE_ACCEL
if ( m_processingShortcut )
{
/* Enter here when a shortcut isn't handled by Qt.
* Return true to avoid Qt-processing of the event
* Instead, use the flag to indicate that it wasn't processed */
m_processingShortcut = false;
return true;
}
#endif // wxUSE_ACCEL
bool handled = false;
// Build the event
@@ -1686,26 +1696,6 @@ bool wxWindowQt::QtHandleKeyEvent ( QWidget *WXUNUSED( handler ), QKeyEvent *eve
// On key presses, send the EVT_CHAR event
if ( !handled && event->type() == QEvent::KeyPress )
{
#if wxUSE_ACCEL
// Check for accelerators
if ( !m_processingShortcut )
{
/* The call to notify() will try to execute a shortcut. If it fails
* it will call keyPressEvent() in our wxQtWidget which calls back
* to this function. We use the m_processingShortcut flag to avoid
* processing that recursive call and return back to this one. */
m_processingShortcut = true;
QApplication::instance()->notify( GetHandle(), event );
handled = m_processingShortcut;
m_processingShortcut = false;
if ( handled )
return true;
}
#endif // wxUSE_ACCEL
// For compatibility with wxMSW, don't generate wxEVT_CHAR event for
// the following keys: SHIFT, CONTROL, MENU, CAPITAL, NUMLOCK and SCROLL.
switch ( event->key() )