fix: Throwing focus and hotkeys to QtCreator

This commit is contained in:
Petr Mironychev
2026-05-15 21:27:45 +02:00
parent cc2d42f6d7
commit b1ca6823b8
11 changed files with 129 additions and 26 deletions

View File

@@ -1,5 +1,10 @@
cmake_minimum_required(VERSION 3.16) 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) project(QodeAssist)
set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOMOC ON)

View File

@@ -94,5 +94,5 @@ target_link_libraries(QodeAssistChatView
) )
target_include_directories(QodeAssistChatView target_include_directories(QodeAssistChatView
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}
) )

View File

@@ -6,12 +6,16 @@
#include <QQmlComponent> #include <QQmlComponent>
#include <QQmlContext> #include <QQmlContext>
#include <QQmlEngine> #include <QQmlEngine>
#include <QQuickItem>
#include <QSettings> #include <QSettings>
#include <QVariantMap> #include <QVariantMap>
#include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
#include <logger/Logger.hpp> #include <logger/Logger.hpp>
#include "QodeAssistConstants.hpp"
namespace { namespace {
constexpr Qt::WindowFlags baseFlags = Qt::Window | Qt::WindowTitleHint | Qt::WindowSystemMenuHint constexpr Qt::WindowFlags baseFlags = Qt::Window | Qt::WindowTitleHint | Qt::WindowSystemMenuHint
| Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint
@@ -39,20 +43,31 @@ ChatView::ChatView(QQmlEngine* engine)
setMinimumSize({400, 300}); setMinimumSize({400, 300});
setFlags(baseFlags); setFlags(baseFlags);
if (auto action = Core::ActionManager::command("QodeAssist.CloseChatView")) { bindCommandShortcut("QodeAssist.CloseChatView", [this] { close(); });
m_closeShortcut = new QShortcut(action->keySequence(), this); bindCommandShortcut(Constants::QODE_ASSIST_CHAT_SEND_MESSAGE, [this] {
connect(m_closeShortcut, &QShortcut::activated, this, &QQuickView::close); QMetaObject::invokeMethod(rootObject(), "sendChatMessage");
});
connect(action, &Core::Command::keySequenceChanged, this, [action, this]() { bindCommandShortcut(Constants::QODE_ASSIST_CHAT_CLEAR_SESSION, [this] {
if (m_closeShortcut) { QMetaObject::invokeMethod(rootObject(), "clearChat");
m_closeShortcut->setKey(action->keySequence()); });
}
});
}
restoreSettings(); restoreSettings();
} }
void ChatView::bindCommandShortcut(Utils::Id commandId,
const std::function<void()> &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) void ChatView::closeEvent(QCloseEvent *event)
{ {
saveSettings(); saveSettings();

View File

@@ -3,6 +3,10 @@
#pragma once #pragma once
#include <functional>
#include <utils/id.h>
#include <QQuickView> #include <QQuickView>
#include <QShortcut> #include <QShortcut>
@@ -27,9 +31,9 @@ protected:
private: private:
void saveSettings(); void saveSettings();
void restoreSettings(); void restoreSettings();
void bindCommandShortcut(Utils::Id commandId, const std::function<void()> &onActivated);
bool m_isPin; bool m_isPin;
QShortcut *m_closeShortcut;
}; };
} // namespace QodeAssist::Chat } // namespace QodeAssist::Chat

View File

@@ -3,8 +3,15 @@
#include "ChatWidget.hpp" #include "ChatWidget.hpp"
#include <QApplication>
#include <QQmlContext> #include <QQmlContext>
#include <QQmlEngine> #include <QQmlEngine>
#include <QQuickItem>
#include <coreplugin/icontext.h>
#include <coreplugin/icore.h>
#include "QodeAssistConstants.hpp"
namespace QodeAssist::Chat { namespace QodeAssist::Chat {
@@ -20,6 +27,12 @@ ChatWidget::ChatWidget(QQmlEngine* engine, QWidget *parent)
setContent(component->url(), component, rootItem); setContent(component->url(), component, rootItem);
} }
setResizeMode(QQuickWidget::SizeRootObjectToView); 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() void ChatWidget::clear()
@@ -31,4 +44,35 @@ void ChatWidget::scrollToBottom()
{ {
QMetaObject::invokeMethod(rootObject(), "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<ChatWidget *>(widget))
return chatWidget;
}
return nullptr;
}
} // namespace QodeAssist::Chat } // namespace QodeAssist::Chat

View File

@@ -17,6 +17,14 @@ public:
Q_INVOKABLE void clear(); Q_INVOKABLE void clear();
Q_INVOKABLE void scrollToBottom(); Q_INVOKABLE void scrollToBottom();
Q_INVOKABLE void focusInput();
void sendMessage();
void clearSession();
bool isChatFocused() const;
static ChatWidget *focusedInstance();
signals: signals:
void clearPressed(); void clearPressed();

View File

@@ -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() { function clearChat() {
root.clearMessages() root.clearMessages()
root.clearAttachmentFiles() root.clearAttachmentFiles()
@@ -545,6 +536,10 @@ ChatRootView {
Qt.callLater(chatListView.positionViewAtEnd) Qt.callLater(chatListView.positionViewAtEnd)
} }
function focusInput() {
messageInput.forceActiveFocus()
}
function applyMentionSelection() { function applyMentionSelection() {
var result = fileMentionPopup.applyCurrentSelection( var result = fileMentionPopup.applyCurrentSelection(
messageInput.text, messageInput.cursorPosition, root.useTools) messageInput.text, messageInput.cursorPosition, root.useTools)
@@ -654,6 +649,6 @@ ChatRootView {
} }
Component.onCompleted: { Component.onCompleted: {
messageInput.forceActiveFocus() focusInput()
} }
} }

View File

@@ -10,4 +10,11 @@ const char MENU_ID[] = "QodeAssist.Menu";
const char QODE_ASSIST_REQUEST_SUGGESTION[] = "QodeAssist.RequestSuggestion"; 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 } // namespace QodeAssist::Constants

View File

@@ -38,18 +38,20 @@ void ChatOutputPane::clearContents()
void ChatOutputPane::visibilityChanged(bool visible) void ChatOutputPane::visibilityChanged(bool visible)
{ {
if (visible) if (visible) {
m_chatWidget->scrollToBottom(); m_chatWidget->scrollToBottom();
m_chatWidget->focusInput();
}
} }
void ChatOutputPane::setFocus() void ChatOutputPane::setFocus()
{ {
m_chatWidget->setFocus(); m_chatWidget->focusInput();
} }
bool ChatOutputPane::hasFocus() const bool ChatOutputPane::hasFocus() const
{ {
return m_chatWidget->hasFocus(); return m_chatWidget->isChatFocused();
} }
bool ChatOutputPane::canFocus() const bool ChatOutputPane::canFocus() const

View File

@@ -4,6 +4,7 @@
#include "NavigationPanel.hpp" #include "NavigationPanel.hpp"
#include "ChatView/ChatWidget.hpp" #include "ChatView/ChatWidget.hpp"
#include "QodeAssistConstants.hpp"
namespace QodeAssist::Chat { namespace QodeAssist::Chat {
@@ -12,7 +13,7 @@ NavigationPanel::NavigationPanel(QQmlEngine* engine)
{ {
setDisplayName(tr("QodeAssist Chat")); setDisplayName(tr("QodeAssist Chat"));
setPriority(500); setPriority(500);
setId("QodeAssistChat"); setId(Constants::QODE_ASSIST_CHAT_NAV_ID);
setActivationSequence(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_C)); setActivationSequence(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_C));
} }

View File

@@ -14,6 +14,7 @@
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h> #include <coreplugin/messagemanager.h>
#include <coreplugin/modemanager.h> #include <coreplugin/modemanager.h>
#include <coreplugin/navigationwidget.h>
#include <coreplugin/statusbarmanager.h> #include <coreplugin/statusbarmanager.h>
#include <extensionsystem/iplugin.h> #include <extensionsystem/iplugin.h>
#include <languageclient/languageclientmanager.h> #include <languageclient/languageclientmanager.h>
@@ -50,6 +51,7 @@
#include "widgets/QuickRefactorDialog.hpp" #include "widgets/QuickRefactorDialog.hpp"
#include <ChatView/ChatView.hpp> #include <ChatView/ChatView.hpp>
#include <ChatView/ChatFileManager.hpp> #include <ChatView/ChatFileManager.hpp>
#include <ChatView/ChatWidget.hpp>
#include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/actionmanager.h>
#include <texteditor/textdocument.h> #include <texteditor/textdocument.h>
@@ -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( Core::ActionContainer *editorContextMenu = Core::ActionManager::actionContainer(
TextEditor::Constants::M_STANDARDCONTEXTMENU); TextEditor::Constants::M_STANDARDCONTEXTMENU);
if (editorContextMenu) { if (editorContextMenu) {