diff --git a/CMakeLists.txt b/CMakeLists.txt index 3eebde3..444e671 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ find_package(Qt6 COMPONENTS Core Gui Quick Widgets Network REQUIRED) add_subdirectory(llmcore) add_subdirectory(settings) add_subdirectory(logger) -add_subdirectory(chatview) +add_subdirectory(ChatView) add_qtc_plugin(QodeAssist PLUGIN_DEPENDS diff --git a/chatview/CMakeLists.txt b/ChatView/CMakeLists.txt similarity index 81% rename from chatview/CMakeLists.txt rename to ChatView/CMakeLists.txt index f2c1f99..ceaa8c5 100644 --- a/chatview/CMakeLists.txt +++ b/ChatView/CMakeLists.txt @@ -1,8 +1,12 @@ qt_add_library(QodeAssistChatView STATIC) +qt_policy(SET QTP0001 NEW) + +# URI name should match the subdirectory name to suppress the warning qt_add_qml_module(QodeAssistChatView URI ChatView VERSION 1.0 + DEPENDENCIES QtQuick QML_FILES qml/RootItem.qml qml/ChatItem.qml @@ -31,5 +35,5 @@ target_link_libraries(QodeAssistChatView ) target_include_directories(QodeAssistChatView - PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ) diff --git a/chatview/ChatModel.cpp b/ChatView/ChatModel.cpp similarity index 100% rename from chatview/ChatModel.cpp rename to ChatView/ChatModel.cpp diff --git a/chatview/ChatModel.hpp b/ChatView/ChatModel.hpp similarity index 98% rename from chatview/ChatModel.hpp rename to ChatView/ChatModel.hpp index 03343e1..af0c312 100644 --- a/chatview/ChatModel.hpp +++ b/ChatView/ChatModel.hpp @@ -24,7 +24,7 @@ #include #include -#include +#include namespace QodeAssist::Chat { diff --git a/chatview/ChatRootView.cpp b/ChatView/ChatRootView.cpp similarity index 100% rename from chatview/ChatRootView.cpp rename to ChatView/ChatRootView.cpp diff --git a/chatview/ChatRootView.hpp b/ChatView/ChatRootView.hpp similarity index 90% rename from chatview/ChatRootView.hpp rename to ChatView/ChatRootView.hpp index 98206f6..c0d9614 100644 --- a/chatview/ChatRootView.hpp +++ b/ChatView/ChatRootView.hpp @@ -28,8 +28,12 @@ namespace QodeAssist::Chat { class ChatRootView : public QQuickItem { - Q_OBJECT - Q_PROPERTY(ChatModel *chatModel READ chatModel NOTIFY chatModelChanged FINAL) + Q_OBJECT + // Possibly Qt bug: QTBUG-131004 + // The class type name must be fully qualified + // including the namespace. + // Otherwise qmlls can't find it. + Q_PROPERTY(QodeAssist::Chat::ChatModel *chatModel READ chatModel NOTIFY chatModelChanged FINAL) Q_PROPERTY(QString currentTemplate READ currentTemplate NOTIFY currentTemplateChanged FINAL) Q_PROPERTY(QColor backgroundColor READ backgroundColor CONSTANT FINAL) Q_PROPERTY(QColor primaryColor READ primaryColor CONSTANT FINAL) diff --git a/chatview/ChatUtils.cpp b/ChatView/ChatUtils.cpp similarity index 100% rename from chatview/ChatUtils.cpp rename to ChatView/ChatUtils.cpp diff --git a/chatview/ChatUtils.h b/ChatView/ChatUtils.h similarity index 100% rename from chatview/ChatUtils.h rename to ChatView/ChatUtils.h diff --git a/chatview/ChatWidget.cpp b/ChatView/ChatWidget.cpp similarity index 94% rename from chatview/ChatWidget.cpp rename to ChatView/ChatWidget.cpp index 7963e5a..c5560ff 100644 --- a/chatview/ChatWidget.cpp +++ b/ChatView/ChatWidget.cpp @@ -27,7 +27,7 @@ namespace QodeAssist::Chat { ChatWidget::ChatWidget(QWidget *parent) : QQuickWidget(parent) { - setSource(QUrl("qrc:/ChatView/qml/RootItem.qml")); + setSource(QUrl("qrc:/qt/qml/ChatView/qml/RootItem.qml")); setResizeMode(QQuickWidget::SizeRootObjectToView); } diff --git a/chatview/ChatWidget.hpp b/ChatView/ChatWidget.hpp similarity index 100% rename from chatview/ChatWidget.hpp rename to ChatView/ChatWidget.hpp diff --git a/chatview/ClientInterface.cpp b/ChatView/ClientInterface.cpp similarity index 100% rename from chatview/ClientInterface.cpp rename to ChatView/ClientInterface.cpp diff --git a/chatview/ClientInterface.hpp b/ChatView/ClientInterface.hpp similarity index 100% rename from chatview/ClientInterface.hpp rename to ChatView/ClientInterface.hpp diff --git a/chatview/MessagePart.hpp b/ChatView/MessagePart.hpp similarity index 100% rename from chatview/MessagePart.hpp rename to ChatView/MessagePart.hpp diff --git a/chatview/qml/Badge.qml b/ChatView/qml/Badge.qml similarity index 100% rename from chatview/qml/Badge.qml rename to ChatView/qml/Badge.qml diff --git a/ChatView/qml/ChatItem.qml b/ChatView/qml/ChatItem.qml new file mode 100644 index 0000000..34b4a17 --- /dev/null +++ b/ChatView/qml/ChatItem.qml @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2024 Petr Mironychev + * + * This file is part of QodeAssist. + * + * QodeAssist is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * QodeAssist is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with QodeAssist. If not, see . + */ + +pragma ComponentBehavior: Bound + +import QtQuick +import ChatView +import "./dialog" + +Rectangle { + id: root + + property alias msgModel: msgCreator.model + property color fontColor + property color codeBgColor + property color selectionColor + + height: msgColumn.height + radius: 8 + + Column { + id: msgColumn + + anchors.verticalCenter: parent.verticalCenter + width: parent.width + spacing: 5 + + Repeater { + id: msgCreator + delegate: Loader { + id: msgCreatorDelegate + // Fix me: + // why does `required property MessagePart modelData` not work? + required property var modelData + + width: parent.width + sourceComponent: { + // If `required property MessagePart modelData` is used + // and conversion to MessagePart fails, you're left + // with a nullptr. This tests that to prevent crashing. + if(!modelData) { + return undefined; + } + + switch(modelData.type) { + case MessagePart.Text: return textComponent; + case MessagePart.Code: return codeBlockComponent; + default: return textComponent; + } + } + + Component { + id: textComponent + TextComponent { + itemData: msgCreatorDelegate.modelData + } + } + + Component { + id: codeBlockComponent + CodeBlockComponent { + itemData: msgCreatorDelegate.modelData + } + } + } + } + } + + component TextComponent : TextBlock { + required property var itemData + height: implicitHeight + 10 + verticalAlignment: Text.AlignVCenter + leftPadding: 10 + text: itemData.text + color: root.fontColor + selectionColor: root.selectionColor + } + + + component CodeBlockComponent : CodeBlock { + required property var itemData + anchors { + left: parent.left + leftMargin: 10 + right: parent.right + rightMargin: 10 + } + + code: itemData.text + language: itemData.language + color: root.codeBgColor + selectionColor: root.selectionColor + } + +} diff --git a/chatview/qml/RootItem.qml b/ChatView/qml/RootItem.qml similarity index 94% rename from chatview/qml/RootItem.qml rename to ChatView/qml/RootItem.qml index 675526b..7f3babf 100644 --- a/chatview/qml/RootItem.qml +++ b/ChatView/qml/RootItem.qml @@ -17,6 +17,8 @@ * along with QodeAssist. If not, see . */ +pragma ComponentBehavior: Bound + import QtQuick import QtQuick.Controls import QtQuick.Controls.Basic as QQC @@ -51,6 +53,7 @@ ChatRootView { cacheBuffer: 2000 delegate: ChatItem { + required property var model width: ListView.view.width - scroll.width msgModel: root.chatModel.processMessageContent(model.content) color: model.roleType === ChatModel.User ? root.primaryColor : root.secondaryColor @@ -71,12 +74,12 @@ ChatRootView { } onCountChanged: { - scrollToBottom() + root.scrollToBottom() } onContentHeightChanged: { if (atYEnd) { - scrollToBottom() + root.scrollToBottom() } } } @@ -103,7 +106,7 @@ ChatRootView { } Keys.onPressed: function(event) { if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && !(event.modifiers & Qt.ShiftModifier)) { - sendChatMessage() + root.sendChatMessage() event.accepted = true; } } @@ -119,7 +122,7 @@ ChatRootView { Layout.alignment: Qt.AlignBottom text: qsTr("Send") - onClicked: sendChatMessage() + onClicked: root.sendChatMessage() } Button { @@ -135,7 +138,7 @@ ChatRootView { Layout.alignment: Qt.AlignBottom text: qsTr("Clear Chat") - onClicked: clearChat() + onClicked: root.clearChat() } CheckBox { diff --git a/chatview/qml/dialog/CodeBlock.qml b/ChatView/qml/dialog/CodeBlock.qml similarity index 98% rename from chatview/qml/dialog/CodeBlock.qml rename to ChatView/qml/dialog/CodeBlock.qml index 4445bfd..99ed84a 100644 --- a/chatview/qml/dialog/CodeBlock.qml +++ b/ChatView/qml/dialog/CodeBlock.qml @@ -61,7 +61,7 @@ Rectangle { text: root.code readOnly: true selectByMouse: true - font.family: monospaceFont + font.family: root.monospaceFont font.pointSize: 12 color: parent.color.hslLightness > 0.5 ? "black" : "white" wrapMode: Text.WordWrap diff --git a/chatview/qml/dialog/TextBlock.qml b/ChatView/qml/dialog/TextBlock.qml similarity index 100% rename from chatview/qml/dialog/TextBlock.qml rename to ChatView/qml/dialog/TextBlock.qml diff --git a/chatview/qml/ChatItem.qml b/chatview/qml/ChatItem.qml deleted file mode 100644 index 457f306..0000000 --- a/chatview/qml/ChatItem.qml +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2024 Petr Mironychev - * - * This file is part of QodeAssist. - * - * QodeAssist is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * QodeAssist is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with QodeAssist. If not, see . - */ - -import QtQuick -import QtQuick.Controls -import ChatView -import "./dialog" - -Rectangle { - id: root - - property alias msgModel: msgCreator.model - property color fontColor - property color codeBgColor - property color selectionColor - - height: msgColumn.height - radius: 8 - - Column { - id: msgColumn - - anchors.verticalCenter: parent.verticalCenter - width: parent.width - spacing: 5 - - Repeater { - id: msgCreator - delegate: Loader { - property var itemData: modelData - - width: parent.width - sourceComponent: { - switch(modelData.type) { - case MessagePart.Text: return textComponent; - case MessagePart.Code: return codeBlockComponent; - default: return textComponent; - } - } - } - } - } - - Component { - id: textComponent - - TextBlock { - height: implicitHeight + 10 - verticalAlignment: Text.AlignVCenter - leftPadding: 10 - text: itemData.text - color: fontColor - selectionColor: root.selectionColor - } - } - - - Component { - id: codeBlockComponent - - CodeBlock { - anchors { - left: parent.left - leftMargin: 10 - right: parent.right - rightMargin: 10 - } - - code: itemData.text - language: itemData.language - color: root.codeBgColor - selectionColor: root.selectionColor - } - } -}