diff --git a/src/app/main.cpp b/src/app/main.cpp
index 9cd2be7f4bb..06d9006083c 100644
--- a/src/app/main.cpp
+++ b/src/app/main.cpp
@@ -36,6 +36,7 @@
#include <QFontDatabase>
#include <QHBoxLayout>
#include <QLibraryInfo>
+#include <QLineEdit>
#include <QMessageBox>
#include <QCryptographicHash>
#include <QNetworkProxyFactory>
@@ -564,6 +565,80 @@ FilePath userResourcePath(const QString &settingsPath, const QString &appId)
return urp;
}
+class ControlHBackspaceFilter : public QObject
+{
+public:
+ explicit ControlHBackspaceFilter(QObject *parentObject = nullptr)
+ : QObject(parentObject)
+ {
+ }
+
+protected:
+ bool eventFilter(QObject *watchedObject, QEvent *event) override
+ {
+ if (event->type() != QEvent::KeyPress && event->type() != QEvent::ShortcutOverride) {
+ return QObject::eventFilter(watchedObject, event);
+ }
+
+ auto *keyEvent = static_cast<QKeyEvent *>(event);
+ const bool hasControl = keyEvent->modifiers() & Qt::ControlModifier;
+ if (hasControl) {
+ qWarning().noquote()
+ << "ControlHBackspaceFilter"
+ << "type=" << int(event->type())
+ << "object=" << watchedObject->metaObject()->className()
+ << "key=" << keyEvent->key()
+ << "modifiers=" << int(keyEvent->modifiers())
+ << "textUcs4=" << [&] {
+ QStringList values;
+ for (const QChar c : keyEvent->text())
+ values << QString::number(c.unicode(), 16);
+ return values.join(QLatin1Char(','));
+ }();
+ }
+
+ const bool isControlH =
+ keyEvent->key() == Qt::Key_H &&
+ (keyEvent->modifiers() & Qt::ControlModifier) &&
+ !(keyEvent->modifiers() & Qt::AltModifier) &&
+ !(keyEvent->modifiers() & Qt::MetaModifier);
+ const bool isControlBackspace =
+ keyEvent->key() == Qt::Key_Backspace &&
+ (keyEvent->modifiers() & Qt::ControlModifier) &&
+ !(keyEvent->modifiers() & Qt::AltModifier) &&
+ !(keyEvent->modifiers() & Qt::MetaModifier);
+
+ if (event->type() == QEvent::ShortcutOverride) {
+ if (isControlH || isControlBackspace) {
+ keyEvent->accept();
+ return false;
+ }
+ return QObject::eventFilter(watchedObject, event);
+ }
+
+ const bool isBackspaceControlCharacter =
+ keyEvent->text() == QString(QChar(0x08));
+
+ if (!isControlH && !isControlBackspace && !isBackspaceControlCharacter) {
+ return QObject::eventFilter(watchedObject, event);
+ }
+
+ if (auto lineEdit = qobject_cast<QLineEdit *>(watchedObject)) {
+ lineEdit->backspace();
+ return true;
+ }
+
+ QKeyEvent backspaceEvent(
+ QEvent::KeyPress,
+ Qt::Key_Backspace,
+ Qt::NoModifier
+ );
+
+ QApplication::sendEvent(watchedObject, &backspaceEvent);
+ return true;
+ }
+};
+
int main(int argc, char **argv)
{
Restarter restarter(argc, argv);
@@ -741,6 +816,9 @@ int main(int argc, char **argv)
QCoreApplication::setOrganizationName(QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR));
QGuiApplication::setApplicationDisplayName(Core::Constants::IDE_DISPLAY_NAME);
+ auto *controlHBackspaceFilter = new ControlHBackspaceFilter(&app);
+ app.installEventFilter(controlHBackspaceFilter);
+
const QScopeGuard cleanup([] { ProcessReaper::deleteAll(); });
const QStringList pluginArguments = app.arguments();
diff --git a/src/app/main_mac.mm b/src/app/main_mac.mm
index 8e1fa5fe253..9d9fb513d25 100644
--- a/src/app/main_mac.mm
+++ b/src/app/main_mac.mm
@@ -3,6 +3,7 @@
#include <qglobal.h>
+#include <AppKit/NSEvent.h>
#include <Foundation/NSNumberFormatter.h>
#include <Foundation/NSUserDefaults.h>
@@ -14,3 +15,38 @@ static void initializeCreatorDefaults()
[NSUserDefaults.standardUserDefaults registerDefaults:@{@"ApplePressAndHoldEnabled": @NO}];
}
Q_CONSTRUCTOR_FUNCTION(initializeCreatorDefaults);
+
+static bool isControlHEvent(NSEvent *event)
+{
+ const NSEventModifierFlags flags = event.modifierFlags & NSEventModifierFlagDeviceIndependentFlagsMask;
+ return event.type == NSEventTypeKeyDown
+ && (flags & NSEventModifierFlagControl)
+ && !(flags & NSEventModifierFlagOption)
+ && !(flags & NSEventModifierFlagCommand)
+ && [[event.charactersIgnoringModifiers lowercaseString] isEqualToString:@"h"];
+}
+
+static NSEvent *backspaceEventFromControlHEvent(NSEvent *event)
+{
+ return [NSEvent keyEventWithType:event.type
+ location:event.locationInWindow
+ modifierFlags:0
+ timestamp:event.timestamp
+ windowNumber:event.windowNumber
+ context:nil
+ characters:@"\b"
+ charactersIgnoringModifiers:@"\b"
+ isARepeat:event.isARepeat
+ keyCode:51];
+}
+
+static void initializeControlHBackspaceMonitor()
+{
+ [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyDown
+ handler:^NSEvent *(NSEvent *event) {
+ if (isControlHEvent(event))
+ return backspaceEventFromControlHEvent(event);
+ return event;
+ }];
+}
+Q_CONSTRUCTOR_FUNCTION(initializeControlHBackspaceMonitor);