本文へスキップします。

本文へ

【全Qt】
【全・Qt】SRAロゴ
H1

FAQ

Q&A:検索

カテゴリー

Q&A:表示

Qt

コア

ウィジェット

QML/Qt Quick

Qt for Python

モデルビュー

画像

ネットワーク

Qt Creator

Windows

Linux

macOS

macOS の Qt Creator で設定やヘルプでキー入力をするときに `^H` を Backspace として 認識させる方法を教えてください。

Qt Creator 20.0.0 のソースツリーに、macOS の Control-H キー入力を Backspace キー入力へ変換するパッチを適用してから Qt Creator をビルドします。

このパッチは、Qt Creator 本体の src/app/main.cppsrc/app/main_mac.mm を変更します。 macOS 側では NSEvent のローカルモニターで Control-H を Backspace 相当のキーイベントに 変換し、Qt 側ではアプリケーション全体のイベントフィルターで QLineEdit などに届く Control-H / Control 付き Backspace / 文字コード 0x08 を Backspace として処理します。

適用例は次のとおりです。

cd <qt-creator-source>
git checkout v20.0.0-beta1
git submodule update --init --recursive
git apply <patch-file>

ここで <patch-file> は、後述のパッチ内容を保存したファイルのパスに置き換えてください。

その後、Qt Creator を通常どおりビルドします。

cmake -S <qt-creator-source> -B <qt-creator-build> \
  -DCMAKE_PREFIX_PATH=<qt-install>/6.11.0/macos \
  -DCMAKE_BUILD_TYPE=RelWithDebInfo
cmake --build <qt-creator-build> --parallel

既存のビルドディレクトリがあり、Qt Creator 本体だけを更新したい場合は、 環境に合わせて Qt Creator のアプリケーションターゲットを再ビルドしてください。

パッチの内容は次のとおりです。

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);

起動後、Qt Creator の設定ダイアログやヘルプの検索欄など、QLineEdit を使っている入力欄で Control-H を押し、カーソル左側の文字が削除されれば反映されています。

参考資料

Qt Creator: ソースからのビルド
Building Qt Creator from Git
Qt Creator source repository
Qt Creator ドキュメント
macOS 用 Qt
macOS で端末から Qt アプリを起動すると端末がアクティブでなくなりキー入力できなくなります。端末でキー入力し続けられるようにするにはどうすればよいですか。

Qt には QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM という環境変数があります。この環境変数を設定すると、Qt アプリを起動したときにアプリをフォアグラウンドアプリケーションへ変換する処理を抑制できます。

macOS 13 では、この環境変数を設定することで端末がアクティブでなくなる問題を解消できます。たとえば、次のようにアプリを起動します。

起動例:
QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM=1 ./MyQtApp

ただし、この方法は macOS のバージョンによって挙動が異なります。macOS 15 では、この環境変数を設定すると Qt アプリが常に非アクティブになり、アプリを操作できなくなってしまいます。

そのため、この環境変数をシェルの初期化ファイルで常に設定することは避けてください。macOS 13 で必要な場合に限り、確認した環境で一時的に設定して起動するのが安全です。

また、この環境変数をプログラム内で qputenv() などを使って設定するのは避けてください。利用者の macOS バージョンによってはアプリを操作できなくなるため、アプリケーション側で一律に設定するべきではありません。

macOS 13 / macOS 15 の違いは外部記事の引用ではなく、確認した環境での挙動です。

参考資料

Qt for macOS - 特定の問題

QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM を参照している Qt のソース

Apple Developer Documentation: LSUIElement

ライセンスモデルに関する FAQ

QtのLGPL版を利用するとソースコードを開示しないといけないのでしょうか。
開発したアプリケーション部分のソースコードを開示する必要はありません。
Qt自身に手を加えるような使い方をした場合に、改変部分のソースコードの開示義務が発生します。
Qtのライブラリにスタティックリンクした場合も改変とみなされ、該当部分が開示対象となります。
その他のLGPLの留意点は以下のページをご覧ください。
LGPL詳細
QtのLGPL版を利用しているのですが、技術的な問合せなどは可能でしょうか。
SRAでは、LGPL版ユーザ様向けのサポートを提供しています。詳細は以下のページをご覧ください。
LGPLサポート詳細

Qtの商用版とLGPL版を併用することは可能でしょうか。
同一プロジェクト内で、Qtの商用版を使用する開発者がいる場合に、他の開発者がLGPL/GPL版のみを使用することはできません。
なお、Qtを使用して作成されたソースコードに共通部分がある場合は、同一プロジェクトとみなされます。
商用版からLGPL版、またはLGPL版から商用版への移行は可能でしょうか。
LGPL 版で開発をした後に商用版に移ることは原則としてできませんが、商用版からLGPL版への移行は可能です。
お問い合わせリンク(FAQ)