From b1ca6823b8809dbd56f2d72e31bfa0b598ad8190 Mon Sep 17 00:00:00 2001 From: Petr Mironychev <9195189+Palm1r@users.noreply.github.com> Date: Fri, 15 May 2026 21:27:45 +0200 Subject: [PATCH] fix: Throwing focus and hotkeys to QtCreator --- CMakeLists.txt | 5 +++++ ChatView/CMakeLists.txt | 2 +- ChatView/ChatView.cpp | 35 ++++++++++++++++++++++--------- ChatView/ChatView.hpp | 6 +++++- ChatView/ChatWidget.cpp | 44 +++++++++++++++++++++++++++++++++++++++ ChatView/ChatWidget.hpp | 8 +++++++ ChatView/qml/RootItem.qml | 15 +++++-------- QodeAssistConstants.hpp | 7 +++++++ chat/ChatOutputPane.cpp | 8 ++++--- chat/NavigationPanel.cpp | 3 ++- qodeassist.cpp | 22 ++++++++++++++++++++ 11 files changed, 129 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e0e6a5..43fc317 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,10 @@ cmake_minimum_required(VERSION 3.16) +# list(APPEND CMAKE_PREFIX_PATH "/Users/palm1r/Qt/Qt Creator 20.0.0-beta1.app/Contents/Resources") +list(APPEND CMAKE_PREFIX_PATH "/Users/palm1r/Qt/Qt Creator 20.0.0-beta1.sdk/lib/cmake/QtCreator") +# list(APPEND CMAKE_PREFIX_PATH "/Users/palm1r/Qt/Qt Creator 20.0.0-beta1.sdk") +# list(APPEND CMAKE_PREFIX_PATH "/Users/palm1r/Qt/Qt Creator 20.0.0-beta1.app/Contents/Resources") + project(QodeAssist) set(CMAKE_AUTOMOC ON) diff --git a/ChatView/CMakeLists.txt b/ChatView/CMakeLists.txt index 34a4db6..f8a3086 100644 --- a/ChatView/CMakeLists.txt +++ b/ChatView/CMakeLists.txt @@ -94,5 +94,5 @@ target_link_libraries(QodeAssistChatView ) target_include_directories(QodeAssistChatView - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR} ) diff --git a/ChatView/ChatView.cpp b/ChatView/ChatView.cpp index 827097b..c94fc4a 100644 --- a/ChatView/ChatView.cpp +++ b/ChatView/ChatView.cpp @@ -6,12 +6,16 @@ #include #include #include +#include #include #include #include +#include #include +#include "QodeAssistConstants.hpp" + namespace { constexpr Qt::WindowFlags baseFlags = Qt::Window | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint @@ -39,20 +43,31 @@ ChatView::ChatView(QQmlEngine* engine) setMinimumSize({400, 300}); setFlags(baseFlags); - if (auto action = Core::ActionManager::command("QodeAssist.CloseChatView")) { - m_closeShortcut = new QShortcut(action->keySequence(), this); - connect(m_closeShortcut, &QShortcut::activated, this, &QQuickView::close); - - connect(action, &Core::Command::keySequenceChanged, this, [action, this]() { - if (m_closeShortcut) { - m_closeShortcut->setKey(action->keySequence()); - } - }); - } + bindCommandShortcut("QodeAssist.CloseChatView", [this] { close(); }); + bindCommandShortcut(Constants::QODE_ASSIST_CHAT_SEND_MESSAGE, [this] { + QMetaObject::invokeMethod(rootObject(), "sendChatMessage"); + }); + bindCommandShortcut(Constants::QODE_ASSIST_CHAT_CLEAR_SESSION, [this] { + QMetaObject::invokeMethod(rootObject(), "clearChat"); + }); restoreSettings(); } +void ChatView::bindCommandShortcut(Utils::Id commandId, + const std::function &onActivated) +{ + auto command = Core::ActionManager::command(commandId); + if (!command) + return; + + auto shortcut = new QShortcut(command->keySequence(), this); + connect(shortcut, &QShortcut::activated, this, onActivated); + connect(command, &Core::Command::keySequenceChanged, shortcut, [command, shortcut]() { + shortcut->setKey(command->keySequence()); + }); +} + void ChatView::closeEvent(QCloseEvent *event) { saveSettings(); diff --git a/ChatView/ChatView.hpp b/ChatView/ChatView.hpp index f1da3ea..baef928 100644 --- a/ChatView/ChatView.hpp +++ b/ChatView/ChatView.hpp @@ -3,6 +3,10 @@ #pragma once +#include + +#include + #include #include @@ -27,9 +31,9 @@ protected: private: void saveSettings(); void restoreSettings(); + void bindCommandShortcut(Utils::Id commandId, const std::function &onActivated); bool m_isPin; - QShortcut *m_closeShortcut; }; } // namespace QodeAssist::Chat diff --git a/ChatView/ChatWidget.cpp b/ChatView/ChatWidget.cpp index 418e36b..c8bf1e4 100644 --- a/ChatView/ChatWidget.cpp +++ b/ChatView/ChatWidget.cpp @@ -3,8 +3,15 @@ #include "ChatWidget.hpp" +#include #include #include +#include + +#include +#include + +#include "QodeAssistConstants.hpp" namespace QodeAssist::Chat { @@ -20,6 +27,12 @@ ChatWidget::ChatWidget(QQmlEngine* engine, QWidget *parent) setContent(component->url(), component, rootItem); } setResizeMode(QQuickWidget::SizeRootObjectToView); + setFocusPolicy(Qt::StrongFocus); + + auto ideContext = new Core::IContext{this}; + ideContext->setWidget(this); + ideContext->setContext(Core::Context{Constants::QODE_ASSIST_CHAT_CONTEXT}); + Core::ICore::addContextObject(ideContext); } void ChatWidget::clear() @@ -31,4 +44,35 @@ void ChatWidget::scrollToBottom() { QMetaObject::invokeMethod(rootObject(), "scrollToBottom"); } + +void ChatWidget::focusInput() +{ + setFocus(Qt::OtherFocusReason); + QMetaObject::invokeMethod(rootObject(), "focusInput"); +} + +bool ChatWidget::isChatFocused() const +{ + return hasFocus() || (rootObject() && rootObject()->hasActiveFocus()); +} + +void ChatWidget::sendMessage() +{ + QMetaObject::invokeMethod(rootObject(), "sendChatMessage"); +} + +void ChatWidget::clearSession() +{ + QMetaObject::invokeMethod(rootObject(), "clearChat"); +} + +ChatWidget *ChatWidget::focusedInstance() +{ + for (QWidget *widget = QApplication::focusWidget(); widget; + widget = widget->parentWidget()) { + if (auto chatWidget = qobject_cast(widget)) + return chatWidget; + } + return nullptr; +} } // namespace QodeAssist::Chat diff --git a/ChatView/ChatWidget.hpp b/ChatView/ChatWidget.hpp index 62c20e2..7bd85d0 100644 --- a/ChatView/ChatWidget.hpp +++ b/ChatView/ChatWidget.hpp @@ -17,6 +17,14 @@ public: Q_INVOKABLE void clear(); Q_INVOKABLE void scrollToBottom(); + Q_INVOKABLE void focusInput(); + + void sendMessage(); + void clearSession(); + + bool isChatFocused() const; + + static ChatWidget *focusedInstance(); signals: void clearPressed(); diff --git a/ChatView/qml/RootItem.qml b/ChatView/qml/RootItem.qml index 490d5e4..6763f95 100644 --- a/ChatView/qml/RootItem.qml +++ b/ChatView/qml/RootItem.qml @@ -526,15 +526,6 @@ ChatRootView { } } - Shortcut { - id: sendMessageShortcut - - sequences: ["Ctrl+Return", "Ctrl+Enter"] - context: Qt.WindowShortcut - enabled: messageInput.activeFocus && !Qt.inputMethod.visible && !fileMentionPopup.visible - onActivated: root.sendChatMessage() - } - function clearChat() { root.clearMessages() root.clearAttachmentFiles() @@ -545,6 +536,10 @@ ChatRootView { Qt.callLater(chatListView.positionViewAtEnd) } + function focusInput() { + messageInput.forceActiveFocus() + } + function applyMentionSelection() { var result = fileMentionPopup.applyCurrentSelection( messageInput.text, messageInput.cursorPosition, root.useTools) @@ -654,6 +649,6 @@ ChatRootView { } Component.onCompleted: { - messageInput.forceActiveFocus() + focusInput() } } diff --git a/QodeAssistConstants.hpp b/QodeAssistConstants.hpp index 7cae9ca..b4446d8 100644 --- a/QodeAssistConstants.hpp +++ b/QodeAssistConstants.hpp @@ -10,4 +10,11 @@ const char MENU_ID[] = "QodeAssist.Menu"; const char QODE_ASSIST_REQUEST_SUGGESTION[] = "QodeAssist.RequestSuggestion"; +const char QODE_ASSIST_CHAT_CONTEXT[] = "QodeAssist.ChatContext"; +const char QODE_ASSIST_CHAT_NAV_ID[] = "QodeAssistChat"; + +const char QODE_ASSIST_CHAT_SEND_MESSAGE[] = "QodeAssist.Chat.SendMessage"; +const char QODE_ASSIST_CHAT_CLEAR_SESSION[] = "QodeAssist.Chat.ClearSession"; +const char QODE_ASSIST_CHAT_SHOW_IN_RIGHT[] = "QodeAssist.Chat.ShowInRightSidebar"; + } // namespace QodeAssist::Constants diff --git a/chat/ChatOutputPane.cpp b/chat/ChatOutputPane.cpp index ecb92e5..ae76955 100644 --- a/chat/ChatOutputPane.cpp +++ b/chat/ChatOutputPane.cpp @@ -38,18 +38,20 @@ void ChatOutputPane::clearContents() void ChatOutputPane::visibilityChanged(bool visible) { - if (visible) + if (visible) { m_chatWidget->scrollToBottom(); + m_chatWidget->focusInput(); + } } void ChatOutputPane::setFocus() { - m_chatWidget->setFocus(); + m_chatWidget->focusInput(); } bool ChatOutputPane::hasFocus() const { - return m_chatWidget->hasFocus(); + return m_chatWidget->isChatFocused(); } bool ChatOutputPane::canFocus() const diff --git a/chat/NavigationPanel.cpp b/chat/NavigationPanel.cpp index c6af855..3317707 100644 --- a/chat/NavigationPanel.cpp +++ b/chat/NavigationPanel.cpp @@ -4,6 +4,7 @@ #include "NavigationPanel.hpp" #include "ChatView/ChatWidget.hpp" +#include "QodeAssistConstants.hpp" namespace QodeAssist::Chat { @@ -12,7 +13,7 @@ NavigationPanel::NavigationPanel(QQmlEngine* engine) { setDisplayName(tr("QodeAssist Chat")); setPriority(500); - setId("QodeAssistChat"); + setId(Constants::QODE_ASSIST_CHAT_NAV_ID); setActivationSequence(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_C)); } diff --git a/qodeassist.cpp b/qodeassist.cpp index 349081a..1ba2856 100644 --- a/qodeassist.cpp +++ b/qodeassist.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +51,7 @@ #include "widgets/QuickRefactorDialog.hpp" #include #include +#include #include #include #include @@ -230,6 +232,26 @@ public: } }); + ActionBuilder sendMessageAction(this, Constants::QODE_ASSIST_CHAT_SEND_MESSAGE); + sendMessageAction.setContext(Core::Context(Constants::QODE_ASSIST_CHAT_CONTEXT)); + sendMessageAction.setText(Tr::tr("Send QodeAssist Chat Message")); + sendMessageAction.setToolTip(Tr::tr("Send the current message to the LLM")); + sendMessageAction.setDefaultKeySequence(QKeySequence(Qt::CTRL | Qt::Key_Return)); + sendMessageAction.addOnTriggered(this, [] { + if (auto chatWidget = Chat::ChatWidget::focusedInstance()) + chatWidget->sendMessage(); + }); + + ActionBuilder clearSessionAction(this, Constants::QODE_ASSIST_CHAT_CLEAR_SESSION); + clearSessionAction.setContext(Core::Context(Constants::QODE_ASSIST_CHAT_CONTEXT)); + clearSessionAction.setText(Tr::tr("Clear QodeAssist Chat Session")); + clearSessionAction.setToolTip(Tr::tr("Clear the current chat session")); + clearSessionAction.setDefaultKeySequence(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_L)); + clearSessionAction.addOnTriggered(this, [] { + if (auto chatWidget = Chat::ChatWidget::focusedInstance()) + chatWidget->clearSession(); + }); + Core::ActionContainer *editorContextMenu = Core::ActionManager::actionContainer( TextEditor::Constants::M_STANDARDCONTEXTMENU); if (editorContextMenu) {