diff --git a/src/plugins/codegeex/builtin/icons/codegeex_model_lite_16px.svg b/src/plugins/codegeex/builtin/icons/codegeex_model_lite_16px.svg new file mode 100644 index 000000000..c44ddd79f --- /dev/null +++ b/src/plugins/codegeex/builtin/icons/codegeex_model_lite_16px.svg @@ -0,0 +1,14 @@ + + + ICON /ai/lite + + + + + + + + + + + \ No newline at end of file diff --git a/src/plugins/codegeex/builtin/icons/codegeex_model_pro_16px.svg b/src/plugins/codegeex/builtin/icons/codegeex_model_pro_16px.svg new file mode 100644 index 000000000..fd9366f10 --- /dev/null +++ b/src/plugins/codegeex/builtin/icons/codegeex_model_pro_16px.svg @@ -0,0 +1,14 @@ + + + ICON /ai/pro + + + + + + + + + + + \ No newline at end of file diff --git a/src/plugins/codegeex/builtin/texts/codegeex_clear_16px.svg b/src/plugins/codegeex/builtin/texts/codegeex_clear_16px.svg index f615e23ad..baf211ccf 100644 --- a/src/plugins/codegeex/builtin/texts/codegeex_clear_16px.svg +++ b/src/plugins/codegeex/builtin/texts/codegeex_clear_16px.svg @@ -1,7 +1,7 @@ ICON / action / clear_history - - + + \ No newline at end of file diff --git a/src/plugins/codegeex/builtin/texts/codegeex_files_16px.svg b/src/plugins/codegeex/builtin/texts/codegeex_files_16px.svg index a1290d8ba..fcc6160b2 100644 --- a/src/plugins/codegeex/builtin/texts/codegeex_files_16px.svg +++ b/src/plugins/codegeex/builtin/texts/codegeex_files_16px.svg @@ -1,7 +1,7 @@ - + ICON /ai/at - + \ No newline at end of file diff --git a/src/plugins/codegeex/builtin/texts/codegeex_history_16px.svg b/src/plugins/codegeex/builtin/texts/codegeex_history_16px.svg index d19d2b987..ad594183f 100644 --- a/src/plugins/codegeex/builtin/texts/codegeex_history_16px.svg +++ b/src/plugins/codegeex/builtin/texts/codegeex_history_16px.svg @@ -1,7 +1,7 @@ - ICON / action /history - - + ICON /ai/history + + \ No newline at end of file diff --git a/src/plugins/codegeex/builtin/texts/codegeex_internet_16px.svg b/src/plugins/codegeex/builtin/texts/codegeex_internet_16px.svg index 22250ecdf..8c0336679 100644 --- a/src/plugins/codegeex/builtin/texts/codegeex_internet_16px.svg +++ b/src/plugins/codegeex/builtin/texts/codegeex_internet_16px.svg @@ -1,7 +1,7 @@ - + ICON /ai/internet - + \ No newline at end of file diff --git a/src/plugins/codegeex/builtin/texts/codegeex_new_16px.svg b/src/plugins/codegeex/builtin/texts/codegeex_new_16px.svg index b5c44ddb6..211467c94 100644 --- a/src/plugins/codegeex/builtin/texts/codegeex_new_16px.svg +++ b/src/plugins/codegeex/builtin/texts/codegeex_new_16px.svg @@ -1,7 +1,7 @@ - ICON / action /add - - + ICON /ai/new_dia + + \ No newline at end of file diff --git a/src/plugins/codegeex/builtin/texts/codegeex_send_16px.svg b/src/plugins/codegeex/builtin/texts/codegeex_send_16px.svg index 867cbc3fd..630d9dae6 100644 --- a/src/plugins/codegeex/builtin/texts/codegeex_send_16px.svg +++ b/src/plugins/codegeex/builtin/texts/codegeex_send_16px.svg @@ -1,7 +1,7 @@ - ICON / action / send - - + ICON /ai/ send + + \ No newline at end of file diff --git a/src/plugins/codegeex/codegeex.qrc b/src/plugins/codegeex/codegeex.qrc index cfeac791f..a39f5ac00 100644 --- a/src/plugins/codegeex/codegeex.qrc +++ b/src/plugins/codegeex/codegeex.qrc @@ -9,6 +9,8 @@ builtin/icons/codegeex_anwser_icon_24px.svg builtin/icons/codegeex_user_30px.svg builtin/icons/codegeex_logo_24px.svg + builtin/icons/codegeex_model_lite_16px.svg + builtin/icons/codegeex_model_pro_16px.svg builtin/texts/codegeex_indicate_16px.svg builtin/texts/codegeex_advice_16px.svg builtin/texts/codegeex_comment_16px.svg diff --git a/src/plugins/codegeex/codegeex/askapi.cpp b/src/plugins/codegeex/codegeex/askapi.cpp index 8a66810ab..bb18677e1 100644 --- a/src/plugins/codegeex/codegeex/askapi.cpp +++ b/src/plugins/codegeex/codegeex/askapi.cpp @@ -322,9 +322,8 @@ QByteArray AskApi::assembleSSEChatBody(const QString &prompt, jsonObject.insert("machineId", machineId); jsonObject.insert("client", "deepin-unioncode"); jsonObject.insert("history", history); - //temp support choose to use later jsonObject.insert("locale", locale); - + if (!CodeGeeXManager::instance()->getReferenceFiles().isEmpty()) { auto fileDatas = parseFile(CodeGeeXManager::instance()->getReferenceFiles()); jsonObject.insert("command", "file_augment"); @@ -336,7 +335,7 @@ QByteArray AskApi::assembleSSEChatBody(const QString &prompt, else jsonObject.insert("model", model); - if(!talkId.isEmpty()) + if (!talkId.isEmpty()) jsonObject.insert("talkId", talkId); return jsonToByteArray(jsonObject); diff --git a/src/plugins/codegeex/codegeexmanager.cpp b/src/plugins/codegeex/codegeexmanager.cpp index bd45b08ea..739b0659c 100644 --- a/src/plugins/codegeex/codegeexmanager.cpp +++ b/src/plugins/codegeex/codegeexmanager.cpp @@ -90,7 +90,6 @@ void CodeGeeXManager::setLocale(CodeGeeX::locale locale) askApi.setLocale("en"); Copilot::instance()->setLocale("en"); } - } void CodeGeeXManager::setCurrentModel(languageModel model) @@ -168,7 +167,7 @@ void CodeGeeXManager::onResponse(const QString &msgID, const QString &data, cons { if (msgID.isEmpty()) return; - + auto msgData = modifiedData(data); if (event == "finish") { responseData.clear(); @@ -190,8 +189,6 @@ void CodeGeeXManager::onResponse(const QString &msgID, const QString &data, cons curSessionMsg[msgID].updateData(responseData); Q_EMIT requestMessageUpdate(curSessionMsg[msgID]); } - } else if (event == "processing") { - emit searching(data); } } @@ -294,8 +291,8 @@ void CodeGeeXManager::initConnections() connect(&askApi, &AskApi::getMessageListResult, this, &CodeGeeXManager::showHistoryMessage); connect(Copilot::instance(), &Copilot::translatingText, this, &CodeGeeXManager::recevieToTranslate); - connect(Copilot::instance(), &Copilot::response, this, &CodeGeeXManager::onResponse); - connect(Copilot::instance(), &Copilot::messageSended, this, &CodeGeeXManager::startReceiving); + connect(Copilot::instance(), &Copilot::response, this, &CodeGeeXManager::onResponse); + connect(Copilot::instance(), &Copilot::messageSended, this, &CodeGeeXManager::startReceiving); connect(this, &CodeGeeXManager::requestStop, &askApi, &AskApi::stopReceive); } diff --git a/src/plugins/codegeex/codegeexmanager.h b/src/plugins/codegeex/codegeexmanager.h index db7007c8b..dc8784be9 100644 --- a/src/plugins/codegeex/codegeexmanager.h +++ b/src/plugins/codegeex/codegeexmanager.h @@ -79,9 +79,9 @@ class CodeGeeXManager : public QObject QList sessionRecords() const; void connectToNetWork(bool connecting); - bool isConnectToNetWork() { return isConnecting; }; - QStringList getReferenceFiles() { return referenceFiles; }; - void setRefereceFiles(QStringList files) { referenceFiles = files; }; + bool isConnectToNetWork() { return isConnecting; } + QStringList getReferenceFiles() { return referenceFiles; } + void setRefereceFiles(const QStringList &files) { referenceFiles = files; } Q_SIGNALS: void loginSuccessed(); diff --git a/src/plugins/codegeex/option/detailwidget.cpp b/src/plugins/codegeex/option/detailwidget.cpp index 08f187a53..c83d972dd 100644 --- a/src/plugins/codegeex/option/detailwidget.cpp +++ b/src/plugins/codegeex/option/detailwidget.cpp @@ -26,7 +26,6 @@ DWIDGET_USE_NAMESPACE static const char *kCodeCompletion = "codeCompletion"; -static const char *kModel = "model"; static const char *kGlobalLanguage = "globalLanguage"; static const char *kCommitsLanguage = "commitsLanguage"; @@ -37,12 +36,10 @@ class DetailWidgetPrivate DCheckBox *cbCodeCompletion = nullptr; DComboBox *globalLanguageBox = nullptr; DComboBox *commitsLanguageBox = nullptr; - DComboBox *modelBox = nullptr; }; DetailWidget::DetailWidget(QWidget *parent) - : PageWidget(parent) - , d(new DetailWidgetPrivate()) + : PageWidget(parent), d(new DetailWidgetPrivate()) { setupUi(); } @@ -81,25 +78,9 @@ void DetailWidget::setupUi() commitsLanguageLayout->addWidget(commitsLabel); commitsLanguageLayout->addWidget(d->commitsLanguageBox); - QHBoxLayout *modelLayout = new QHBoxLayout; - DLabel *modelLabel = new DLabel(QLabel::tr("model"), this); - d->modelBox = new DComboBox(this); - d->modelBox->addItem("CodeGeex Lite", CodeGeeX::Lite); - d->modelBox->addItem("CodeGeex Pro", CodeGeeX::Pro); - modelLayout->addWidget(modelLabel); - modelLayout->addWidget(d->modelBox); - - connect(d->modelBox, &DComboBox::currentTextChanged, this, [=](const QString &text){ - if (text == "CodeGeex Lite") - d->modelBox->setToolTip(tr("Quickly Answer Questions")); - else if (text == "CodeGeex Pro") - d->modelBox->setToolTip(tr("Provide More Accurate Answers to Technical Questions")); - }); - vLayout->addLayout(completionLayout); vLayout->addLayout(languageLayout); vLayout->addLayout(commitsLanguageLayout); - vLayout->addLayout(modelLayout); vLayout->addStretch(); } @@ -107,7 +88,6 @@ bool DetailWidget::getControlValue(QMap &map) { CodeGeeXConfig config; config.codeCompletionEnabled = d->cbCodeCompletion->isChecked(); - config.model = d->modelBox->currentData().value(); config.globalLanguage = d->globalLanguageBox->currentData().value(); config.commitsLanguage = d->commitsLanguageBox->currentData().value(); dataToMap(config, map); @@ -115,7 +95,6 @@ bool DetailWidget::getControlValue(QMap &map) Copilot::instance()->setGenerateCodeEnabled(config.codeCompletionEnabled); Copilot::instance()->setCommitsLocale(config.commitsLanguage == CodeGeeX::Zh ? "zh" : "en"); CodeGeeXManager::instance()->setLocale(config.globalLanguage); - CodeGeeXManager::instance()->setCurrentModel(config.model); return true; } @@ -126,11 +105,6 @@ void DetailWidget::setControlValue(const QMap &map) mapToData(map, config); d->cbCodeCompletion->setChecked(config.codeCompletionEnabled); - for (auto index = 0; index < d->modelBox->count(); index++) { - if (d->modelBox->itemData(index) == config.model) - d->modelBox->setCurrentIndex(index); - } - d->globalLanguageBox->setCurrentText(config.globalLanguage == CodeGeeX::Zh ? "简体中文" : "English"); d->commitsLanguageBox->setCurrentText(config.commitsLanguage == CodeGeeX::Zh ? "简体中文" : "English"); } @@ -139,7 +113,6 @@ bool DetailWidget::dataToMap(const CodeGeeXConfig &config, QMap apiKey; apiKey.insert(kCodeCompletion, config.codeCompletionEnabled); - apiKey.insert(kModel, config.model); apiKey.insert(kGlobalLanguage, config.globalLanguage); apiKey.insert(kCommitsLanguage, config.commitsLanguage); @@ -154,9 +127,6 @@ bool DetailWidget::mapToData(const QMap &map, CodeGeeXConfig auto var = detail.value(kCodeCompletion); if (var.isValid()) config.codeCompletionEnabled = var.toBool(); - var = detail.value(kModel); - if (var.isValid()) - config.model = var.value(); var = detail.value(kGlobalLanguage); if (var.isValid()) config.globalLanguage = var.value(); @@ -164,7 +134,6 @@ bool DetailWidget::mapToData(const QMap &map, CodeGeeXConfig if (var.isValid()) config.commitsLanguage = var.value(); - return true; } diff --git a/src/plugins/codegeex/widgets/askpagewidget.cpp b/src/plugins/codegeex/widgets/askpagewidget.cpp index 157a967b5..356190d76 100644 --- a/src/plugins/codegeex/widgets/askpagewidget.cpp +++ b/src/plugins/codegeex/widgets/askpagewidget.cpp @@ -6,7 +6,6 @@ #include "intropage.h" #include "messagecomponent.h" #include "codegeexmanager.h" -#include "services/editor/editorservice.h" #include #include @@ -21,44 +20,10 @@ #include #include #include -#include -#include +#include -static const int minInputEditHeight = 36; -static const int maxInputEditHeight = 236; -static const int minInputWidgetHeight = 86; - -InputEdit::InputEdit(QWidget *parent) - : DTextEdit(parent) -{ - setMinimumHeight(minInputEditHeight); - setFixedHeight(minInputEditHeight); - setLineWrapMode(QTextEdit::WidgetWidth); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - - connect(this, &DTextEdit::textChanged, this, [this]() { - auto adjustHeight = document()->size().height(); - if (adjustHeight < minInputEditHeight) - setFixedHeight(minInputEditHeight); - else if (adjustHeight > maxInputEditHeight) - setFixedHeight(maxInputEditHeight); - else - setFixedHeight(adjustHeight); - }); -} - -void InputEdit::keyPressEvent(QKeyEvent *e) -{ - if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { - if (e->modifiers() & Qt::AltModifier) - insertPlainText("\n"); - else if (!document()->toPlainText().isEmpty()) - emit pressedEnter(); - return; - } - - DTextEdit::keyPressEvent(e); -} +// button height + margin +static const int inputExtraHeight = 56; AskPageWidget::AskPageWidget(QWidget *parent) : DWidget(parent) @@ -112,7 +77,7 @@ void AskPageWidget::onMessageUpdate(const MessageData &msgData) void AskPageWidget::slotMessageSend() { if (inputEdit) { - auto prompt = inputEdit->toPlainText(); + auto prompt = inputEdit->edit()->toPlainText(); if (prompt.isEmpty()) return; askQuestion(prompt); @@ -142,22 +107,6 @@ void AskPageWidget::onDeleteBtnClicked() confirmDialog->exec(); } -void AskPageWidget::onReferenceBtnClicked() -{ - referenceBtn->setChecked(true); - referenceMenu->exec(QCursor::pos()); - referenceBtn->update(); - if (selectedFiles.isEmpty()) - referenceBtn->setChecked(false); - - CodeGeeXManager::instance()->setRefereceFiles(selectedFiles); -} - -void AskPageWidget::onNetWorkBtnClicked() -{ - CodeGeeXManager::instance()->connectToNetWork(netWorkBtn->isChecked()); -} - void AskPageWidget::onHistoryBtnClicked() { Q_EMIT requestShowHistoryPage(); @@ -169,6 +118,12 @@ void AskPageWidget::onCreateNewBtnClicked() CodeGeeXManager::instance()->createNewSession(); } +void AskPageWidget::onModelchanged(int index) +{ + auto model = modelCb->itemData(index).value(); + CodeGeeXManager::instance()->setCurrentModel(model); +} + void AskPageWidget::initUI() { QVBoxLayout *layout = new QVBoxLayout; @@ -208,7 +163,7 @@ void AskPageWidget::initUI() void AskPageWidget::initInputWidget() { QVBoxLayout *layout = new QVBoxLayout; - layout->setContentsMargins(10, 0, 10, 0); + layout->setContentsMargins(10, 0, 10, 10); layout->setSpacing(0); inputWidget->setLayout(layout); @@ -217,59 +172,40 @@ void AskPageWidget::initInputWidget() btnLayout->setContentsMargins(0, 0, 0, 0); deleteBtn = new DToolButton(this); - deleteBtn->setFixedSize(30, 30); + deleteBtn->setFixedSize(26, 26); deleteBtn->setIcon(QIcon::fromTheme("codegeex_clear")); deleteBtn->setToolTip(tr("delete this session")); - referenceBtn = new DToolButton(this); - referenceBtn->setFixedSize(30, 30); - referenceBtn->setIcon(QIcon::fromTheme("codegeex_files")); - referenceBtn->setToolTip(tr("reference files")); - referenceBtn->setCheckable(true); - - netWorkBtn = new DToolButton(this); - netWorkBtn->setFixedSize(30, 30); - netWorkBtn->setCheckable(true); - netWorkBtn->setIcon(QIcon::fromTheme("codegeex_internet")); - netWorkBtn->setToolTip(tr("connect to network")); - btnLayout->addWidget(deleteBtn); - btnLayout->addWidget(referenceBtn); - btnLayout->addWidget(netWorkBtn); - - btnLayout->addStretch(1); historyBtn = new DToolButton(this); historyBtn->setIcon(QIcon::fromTheme("codegeex_history")); - historyBtn->setFixedSize(30, 30); + historyBtn->setFixedSize(26, 26); historyBtn->setToolTip(tr("history sessions")); btnLayout->addWidget(historyBtn); createNewBtn = new DToolButton(this); createNewBtn->setIcon(QIcon::fromTheme("codegeex_new")); - createNewBtn->setFixedSize(30, 30); + createNewBtn->setFixedSize(26, 26); createNewBtn->setToolTip(tr("create new session")); btnLayout->addWidget(createNewBtn); + btnLayout->addStretch(1); - auto hlayout = new QHBoxLayout; - inputEdit = new InputEdit(inputWidget); - placeHolderText = tr("Ask question here, press Enter to send..."); - inputEdit->setPlaceholderText(placeHolderText); + modelCb = new QComboBox(this); + modelCb->setFixedHeight(26); + modelCb->addItem(QIcon::fromTheme("codegeex_model_lite"), "Lite", CodeGeeX::languageModel::Lite); + modelCb->addItem(QIcon::fromTheme("codegeex_model_pro"), "Pro", CodeGeeX::languageModel::Pro); + modelCb->setFixedWidth(100); + btnLayout->addWidget(modelCb); - sendButton = new DFloatingButton(this); - sendButton->setFixedSize(30, 30); - sendButton->setIcon(QIcon::fromTheme("codegeex_send").pixmap(16, QIcon::Selected)); - sendButton->setEnabled(false); + inputEdit = new InputEditWidget(inputWidget); + placeHolderText = tr("Ask question here, press Enter to send..."); + inputEdit->edit()->setPlaceholderText(placeHolderText); - inputWidget->setFixedHeight(minInputWidgetHeight); + inputWidget->setFixedHeight(inputEdit->height() + inputExtraHeight); inputWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - hlayout->addWidget(inputEdit); - hlayout->setSpacing(10); - hlayout->addWidget(sendButton); - layout->addLayout(hlayout); - - initReferenceMenu(); + layout->addWidget(inputEdit); } void AskPageWidget::initConnection() @@ -280,20 +216,14 @@ void AskPageWidget::initConnection() connect(CodeGeeXManager::instance(), &CodeGeeXManager::terminated, this, &AskPageWidget::onChatFinished); connect(CodeGeeXManager::instance(), &CodeGeeXManager::setTextToSend, this, &AskPageWidget::setInputText); - connect(sendButton, &DFloatingButton::clicked, this, &AskPageWidget::slotMessageSend); - connect(inputEdit, &InputEdit::pressedEnter, this, &AskPageWidget::slotMessageSend); + connect(inputEdit, &InputEditWidget::messageSended, this, &AskPageWidget::slotMessageSend); + connect(inputEdit, &InputEditWidget::pressedEnter, this, &AskPageWidget::slotMessageSend); connect(deleteBtn, &DToolButton::clicked, this, &AskPageWidget::onDeleteBtnClicked); - connect(referenceBtn, &DToolButton::clicked, this, &AskPageWidget::onReferenceBtnClicked); - connect(netWorkBtn, &DToolButton::clicked, this, &AskPageWidget::onNetWorkBtnClicked); connect(historyBtn, &DToolButton::clicked, this, &AskPageWidget::onHistoryBtnClicked); connect(createNewBtn, &DToolButton::clicked, this, &AskPageWidget::onCreateNewBtnClicked); - connect(inputEdit, &DTextEdit::textChanged, this, [this]() { - if (inputEdit->toPlainText().isEmpty()) - sendButton->setEnabled(false); - else - sendButton->setEnabled(true); - - inputWidget->setFixedHeight(inputEdit->height() + minInputWidgetHeight - minInputEditHeight); + connect(modelCb, qOverload(&QComboBox::currentIndexChanged), this, &AskPageWidget::onModelchanged); + connect(inputEdit->edit(), &DTextEdit::textChanged, this, [this]() { + inputWidget->setFixedHeight(inputEdit->height() + inputExtraHeight); }); connect(stopGenerate, &DPushButton::clicked, this, [this]() { CodeGeeXManager::instance()->stopReceiving(); @@ -313,59 +243,6 @@ void AskPageWidget::initConnection() }); } -void AskPageWidget::initReferenceMenu() -{ - // files references menu - referenceMenu = new QMenu(this); - auto *current = new QAction(tr("Current file"), this); - current->setCheckable(true); - auto *opened = new QAction(tr("Opened files"), this); - opened->setCheckable(true); - auto *select = new QAction(tr("Select file"), this); - select->setCheckable(true); - - auto *clear = new QAction(tr("clear"), this); - - QActionGroup *group = new QActionGroup(this); - group->addAction(current); - group->addAction(opened); - group->addAction(select); - - referenceMenu->addActions(group->actions()); - referenceMenu->addSeparator(); - referenceMenu->addAction(clear); - - auto editorSrv = dpfGetService(dpfservice::EditorService); - - connect(current, &QAction::triggered, this, [=](){ - selectedFiles.clear(); - auto file = editorSrv->currentFile(); - if (file.isEmpty()) - current->setChecked(false); - else - selectedFiles.append(file); - }); - connect(opened, &QAction::triggered, this, [=](){ - selectedFiles.clear(); - selectedFiles = editorSrv->openedFiles(); - if (selectedFiles.isEmpty()) - opened->setChecked(false); - }); - connect(select, &QAction::triggered, this, [=](){ - selectedFiles.clear(); - QString result = QFileDialog::getOpenFileName(this, tr("Select File"), QDir::homePath()); - if (result.isEmpty()) - select->setChecked(false); - else - selectedFiles.append(result); - }); - connect(clear, &QAction::triggered, this, [=](){ - selectedFiles.clear(); - referenceBtn->setChecked(false); - group->checkedAction()->setChecked(false); - }); -} - void AskPageWidget::cleanWidgets() { if (auto currentWidget = scrollArea->takeWidget()) { @@ -400,7 +277,7 @@ void AskPageWidget::enterAnswerState() } progressCalcNum = 0; - inputEdit->clear(); + inputEdit->edit()->clear(); inputEdit->setEnabled(false); if (deleteBtn) @@ -418,7 +295,7 @@ void AskPageWidget::enterInputState() { stopWidget->hide(); inputEdit->setEnabled(true); - inputEdit->setPlaceholderText(placeHolderText); + inputEdit->edit()->setPlaceholderText(placeHolderText); if (deleteBtn) deleteBtn->setEnabled(true); @@ -455,5 +332,5 @@ void AskPageWidget::resetBtns() void AskPageWidget::setInputText(const QString &prompt) { if (!waitingAnswer) - inputEdit->setText(prompt); + inputEdit->edit()->setText(prompt); } diff --git a/src/plugins/codegeex/widgets/askpagewidget.h b/src/plugins/codegeex/widgets/askpagewidget.h index 2684cfb8b..b3427888b 100644 --- a/src/plugins/codegeex/widgets/askpagewidget.h +++ b/src/plugins/codegeex/widgets/askpagewidget.h @@ -6,11 +6,11 @@ #define ASKPAGEWIDGET_H #include "data/messagedata.h" +#include "inputeditwidget.h" #include #include #include -#include #include #include @@ -21,19 +21,6 @@ class QPushButton; class QLineEdit; QT_END_NAMESPACE -class InputEdit : public DTK_WIDGET_NAMESPACE::DTextEdit -{ - Q_OBJECT -public: - explicit InputEdit(QWidget *parent = nullptr); - -signals: - void pressedEnter(); - -protected: - void keyPressEvent(QKeyEvent *e) override; -}; - class MessageComponent; class AskPageWidget : public DTK_WIDGET_NAMESPACE::DWidget { @@ -58,17 +45,15 @@ public Q_SLOTS: void slotMessageSend(); void onChatFinished(); void onDeleteBtnClicked(); - void onReferenceBtnClicked(); - void onNetWorkBtnClicked(); void onHistoryBtnClicked(); void onCreateNewBtnClicked(); + void onModelchanged(int index); void setInputText(const QString &prompt); private: void initUI(); void initInputWidget(); void initConnection(); - void initReferenceMenu(); void cleanWidgets(); void setSessionPage(); @@ -83,15 +68,12 @@ public Q_SLOTS: DTK_WIDGET_NAMESPACE::DScrollArea *scrollArea { nullptr }; DTK_WIDGET_NAMESPACE::DWidget *inputWidget { nullptr }; DTK_WIDGET_NAMESPACE::DWidget *messageContainer { nullptr }; - InputEdit *inputEdit { nullptr }; + InputEditWidget *inputEdit { nullptr }; - DTK_WIDGET_NAMESPACE::DFloatingButton *sendButton { nullptr }; DTK_WIDGET_NAMESPACE::DToolButton *deleteBtn { nullptr }; - DTK_WIDGET_NAMESPACE::DToolButton *referenceBtn { nullptr }; - QMenu *referenceMenu { nullptr }; - DTK_WIDGET_NAMESPACE::DToolButton *netWorkBtn { nullptr }; DTK_WIDGET_NAMESPACE::DToolButton *historyBtn { nullptr }; DTK_WIDGET_NAMESPACE::DToolButton *createNewBtn { nullptr }; + QComboBox *modelCb { nullptr }; DTK_WIDGET_NAMESPACE::DWidget *stopWidget { nullptr }; DTK_WIDGET_NAMESPACE::DPushButton *stopGenerate { nullptr }; @@ -103,7 +85,6 @@ public Q_SLOTS: PageState curState; bool waitingAnswer { false }; bool isConnecting { false }; - QStringList selectedFiles; }; #endif // ASKPAGEWIDGET_H diff --git a/src/plugins/codegeex/widgets/inputeditwidget.cpp b/src/plugins/codegeex/widgets/inputeditwidget.cpp new file mode 100644 index 000000000..4f336d544 --- /dev/null +++ b/src/plugins/codegeex/widgets/inputeditwidget.cpp @@ -0,0 +1,464 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "inputeditwidget.h" +#include "referencepopup.h" +#include "codegeexmanager.h" +#include "services/editor/editorservice.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +DWIDGET_USE_NAMESPACE + +static const int minInputEditHeight = 36; +static const int maxInputEditHeight = 236; + +TagTextFormat::TagTextFormat() + : QTextCharFormat(QTextFormat(QTextFormat::InvalidFormat)) +{ + setObjectType(QTextFormat::UserObject + 1); +} + +void TagTextFormat::setText(const QString &text) +{ + setProperty(QTextFormat::UserProperty, text); +} + +TagTextFormat::TagTextFormat(const QTextFormat &fmt) + : QTextCharFormat(fmt) +{ +} + +class TagObjectInterface : public QObject, public QTextObjectInterface +{ + Q_OBJECT + Q_INTERFACES(QTextObjectInterface) + +public: + QSizeF intrinsicSize(QTextDocument *doc, int posInDocument, + const QTextFormat &format) override + { + Q_UNUSED(doc); + Q_UNUSED(posInDocument); + const TagTextFormat tagFormat(format); + const QFontMetricsF fm(tagFormat.font()); + return QSizeF(fm.horizontalAdvance(tagFormat.text()) + 5, fm.height()); + } + + void drawObject(QPainter *painter, const QRectF &rect, QTextDocument *doc, + int posInDocument, const QTextFormat &format) override + { + Q_UNUSED(doc); + Q_UNUSED(posInDocument); + const TagTextFormat tagFormat(format); + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + + using DTK_GUI_NAMESPACE::DPalette; + + DPalette dp(DGuiApplicationHelper::instance()->applicationPalette()); + + auto color = dp.color(DPalette::LightLively); + color.setAlpha(26); + painter->setBrush(color); + + const QFontMetricsF fontMetrics(tagFormat.font()); + QRectF tagRect(rect.x(), rect.y(), rect.width(), fontMetrics.height() + 5); + painter->setPen(Qt::NoPen); + painter->drawRoundedRect(tagRect, 5, 5); + QPen pen(dp.color(DPalette::LightLively)); + pen.setWidth(0); + painter->setPen(pen); + painter->drawText(tagRect, Qt::AlignCenter, tagFormat.text()); + painter->restore(); + } +}; + +QString TagTextFormat::text() const +{ + return property(QTextFormat::UserProperty).toString(); +} + +class InputEditWidgetPrivate +{ +public: + explicit InputEditWidgetPrivate(InputEditWidget *qq); + InputEditWidget *q; + InputEdit *edit { nullptr }; + DToolButton *sendButton { nullptr }; + DToolButton *netWorkBtn { nullptr }; + DToolButton *referenceBtn { nullptr }; + + QWidget *buttonBox { nullptr }; + + PopupWidget *referencePopup { nullptr }; + QList defaultReferenceItems; + ItemModel model; + + QStringList selectedFiles; + QMap tagMap; + +private: + void initEdit(); + void initButtonBox(); + void initreferencePopup(); +}; + +InputEditWidgetPrivate::InputEditWidgetPrivate(InputEditWidget *qq) + : q(qq) +{ + initEdit(); + initButtonBox(); + initreferencePopup(); +} + +void InputEditWidgetPrivate::initEdit() +{ + edit = new InputEdit(q); + InputEditWidget::connect(edit, &InputEdit::textChanged, q, [this]() { + if (edit->toPlainText().isEmpty()) + sendButton->setEnabled(false); + else + sendButton->setEnabled(true); + + q->setFixedHeight(edit->height() + buttonBox->height()); + auto cursorPos = edit->textCursor().position(); + if (cursorPos > 0 && edit->document()->characterAt(cursorPos - 1) == "@") + q->popupReference(); + }); +} + +void InputEditWidgetPrivate::initButtonBox() +{ + buttonBox = new QWidget(q); + buttonBox->setFixedHeight(minInputEditHeight); + auto hLayout = new QHBoxLayout(buttonBox); + hLayout->setContentsMargins(6, 6, 6, 6); + hLayout->setAlignment(Qt::AlignRight); + hLayout->setSpacing(0); + + sendButton = new DToolButton(q); + sendButton->setFixedSize(24, 24); + sendButton->setIcon(QIcon::fromTheme("codegeex_send")); + sendButton->setEnabled(false); + + referenceBtn = new DToolButton(q); + referenceBtn->setFixedSize(24, 24); + referenceBtn->setIcon(QIcon::fromTheme("codegeex_files")); + referenceBtn->setToolTip(InputEditWidget::tr("reference files")); + + netWorkBtn = new DToolButton(q); + netWorkBtn->setFixedSize(24, 24); + netWorkBtn->setCheckable(true); + netWorkBtn->setIcon(QIcon::fromTheme("codegeex_internet")); + netWorkBtn->setToolTip(InputEditWidget::tr("connect to network")); + + InputEditWidget::connect(sendButton, &DToolButton::clicked, q, &InputEditWidget::messageSended); + InputEditWidget::connect(referenceBtn, &DToolButton::clicked, q, &InputEditWidget::onReferenceBtnClicked); + InputEditWidget::connect(netWorkBtn, &DToolButton::clicked, q, &InputEditWidget::onNetWorkBtnClicked); + hLayout->addWidget(referenceBtn); + hLayout->addWidget(netWorkBtn); + hLayout->addWidget(sendButton); +} + +void InputEditWidgetPrivate::initreferencePopup() +{ + referencePopup = new PopupWidget(q); + referencePopup->setWindowFlags(Qt::ToolTip); + referencePopup->setmodel(&model); + + ItemInfo currentFile; + currentFile.type = "CurrentFile"; + currentFile.displayName = InputEditWidget::tr("Current File"); + ItemInfo selectFile; + selectFile.type = "SelectFile"; + selectFile.displayName = InputEditWidget::tr("Select File"); + ItemInfo openedFiles; + openedFiles.type = "OpenedFiles"; + openedFiles.displayName = InputEditWidget::tr("Opened Files"); + + defaultReferenceItems = QList { currentFile, selectFile, openedFiles }; +} + +InputEdit::InputEdit(QWidget *parent) + : DTextEdit(parent) +{ + setMinimumHeight(minInputEditHeight); + setFixedHeight(minInputEditHeight); + setLineWrapMode(QTextEdit::WidgetWidth); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + document()->documentLayout()->registerHandler(QTextFormat::UserObject + 1, new TagObjectInterface); + + connect(this, &DTextEdit::textChanged, this, &InputEdit::onTextChanged); +} + +void InputEdit::onTextChanged() +{ + auto adjustHeight = document()->size().height(); + if (adjustHeight < minInputEditHeight) + setFixedHeight(minInputEditHeight); + else if (adjustHeight > maxInputEditHeight) + setFixedHeight(maxInputEditHeight); + else + setFixedHeight(adjustHeight); + + QTextCursor cursor(document()); + QSet tagList; + int last_pos = 0; + + cursor.setPosition(0); + formatList.clear(); + + while (!cursor.atEnd()) { + cursor.setPosition(last_pos + 1); + + if (last_pos == cursor.position()) + break; + last_pos = cursor.position(); + + TagTextFormat format(cursor.charFormat()); + if (format.objectType() == QTextFormat::UserObject + 1) { + const QString &text = format.text(); + + if (!text.isEmpty()) { + tagList << text; + formatList << text; + + if (!formats.contains(text)) { + formats[text] = format; + Q_EMIT tagAdded(text); + } + } + } + } + + Q_FOREACH (const TagTextFormat &f, formats) { + if (!tagList.contains(f.text())) { + formats.remove(f.text()); + Q_EMIT tagRemoved(f.text()); + } + } +} + +bool InputEdit::event(QEvent *e) +{ + if (e->type() == QEvent::Paint) + return true; // do not show border when get focus + return DTextEdit::event(e); +} + +void InputEdit::keyPressEvent(QKeyEvent *e) +{ + if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { + if (e->modifiers() & Qt::AltModifier) + insertPlainText("\n"); + return; + } + + DTextEdit::keyPressEvent(e); +} + +void InputEdit::focusOutEvent(QFocusEvent *e) +{ + DTextEdit::focusOutEvent(e); + emit focusOut(); +} + +QString InputEdit::toPlainText() const +{ + return DTextEdit::toPlainText().remove(QChar::ObjectReplacementCharacter); +} + +void InputEdit::appendTag(const QString &text) +{ + auto currentCursor = textCursor(); + while (currentCursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor)) { + if (currentCursor.selectedText().at(0) == "@") + break; + } + + auto selectedText = currentCursor.selectedText(); + if (selectedText.startsWith('@')) + currentCursor.removeSelectedText(); + + auto tagText = "@" + text; + + auto oldFormat = textCursor().charFormat(); + TagTextFormat format; + format.setText(tagText); + formats.insert(tagText, format); + + textCursor().insertText(QString(QChar::ObjectReplacementCharacter), format); + textCursor().insertText(QString(QChar::ObjectReplacementCharacter), oldFormat); // to reset textCharFormat +} + +void InputEditWidget::onReferenceBtnClicked() +{ + d->edit->append("@"); +} + +void InputEditWidget::onNetWorkBtnClicked() +{ + CodeGeeXManager::instance()->connectToNetWork(d->netWorkBtn->isChecked()); +} + +InputEditWidget::InputEditWidget(QWidget *parent) + : DFrame(parent), d(new InputEditWidgetPrivate(this)) +{ + auto mainLayout = new QVBoxLayout(this); + mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->addWidget(d->edit); + mainLayout->addWidget(d->buttonBox); + installEventFilter(this); + d->edit->installEventFilter(this); + + setFixedHeight(d->edit->height() + d->buttonBox->height()); + connect(this, &InputEditWidget::handleKey, d->referencePopup, &PopupWidget::keyPressEvent); + connect(d->edit, &InputEdit::enterReference, this, &InputEditWidget::popupReference); + connect(d->referencePopup, &PopupWidget::selectIndex, this, &InputEditWidget::accept); + connect(d->edit, &InputEdit::focusOut, d->referencePopup, &PopupWidget::hide); + connect(d->edit, &InputEdit::tagAdded, this, &InputEditWidget::onTagAdded); + connect(d->edit, &InputEdit::tagRemoved, this, &InputEditWidget::onTagRemoved); +} + +bool InputEditWidget::event(QEvent *e) +{ + if (e->type() == QEvent::Paint) { + QPainter p(this); + p.setRenderHints(QPainter::Antialiasing); + + QStyleOptionFrame panel; + initStyleOption(&panel); + style()->drawPrimitive(QStyle::PE_PanelLineEdit, &panel, &p, this); + + return true; + } + + return DFrame::event(e); +} + +bool InputEditWidget::eventFilter(QObject *watched, QEvent *event) +{ + if (watched == d->edit && event->type() == QEvent::KeyPress) { + auto keyEvent = static_cast(event); + switch (keyEvent->key()) { + case Qt::Key_PageUp: + case Qt::Key_PageDown: + case Qt::Key_Down: + case Qt::Key_Tab: + case Qt::Key_Up: + case Qt::Key_Backtab: + emit handleKey(keyEvent); + return true; + case Qt::Key_Enter: + case Qt::Key_Return: + if (!d->referencePopup->isVisible()) + emit pressedEnter(); + else + emit handleKey(keyEvent); + return true; + case Qt::Key_Backspace: + case Qt::Key_Space: + d->referencePopup->hide(); + break; + default: + break; + } + } + return QObject::eventFilter(watched, event); +} + +QTextEdit *InputEditWidget::edit() +{ + return d->edit; +} + +void InputEditWidget::popupReference() +{ + d->model.clear(); + d->tagMap.clear(); + d->model.addItems(d->defaultReferenceItems); + d->referencePopup->show(); + d->referencePopup->selectFirstRow(); +} + +void InputEditWidget::accept(const QModelIndex &index) +{ + if (!index.isValid()) + return; + auto row = index.row(); + if (row < 0 || row >= d->model.rowCount()) + return; + + using dpfservice::EditorService; + EditorService *editorSrv = dpfGetService(EditorService); + ItemInfo item = d->model.getItems().at(row); + + auto appendTag = [=](const QString &filePath) { + QFileInfo info(filePath); + d->selectedFiles.append(filePath); + auto tag = "file: " + info.dir().dirName() + '/' + info.fileName(); + d->edit->appendTag(tag); + d->tagMap.insert('@' + tag, { filePath }); + }; + if (item.type == "CurrentFile") { + auto filePath = editorSrv->currentFile(); + if (filePath.isEmpty()) + return; + appendTag(filePath); + } else if (item.type == "SelectFile") { + QString result = QFileDialog::getOpenFileName(this, QAction::tr("Select File"), QDir::homePath()); + if (result.isEmpty()) + return; + appendTag(result); + } else if (item.type == "OpenedFiles") { + auto openedFiles = editorSrv->openedFiles(); + if (openedFiles.isEmpty()) + return; + QList items; + for (auto file : openedFiles) { + ItemInfo item; + item.extraInfo = file; + item.displayName = QFileInfo(file).fileName(); + items.append(item); + } + d->model.clear(); + d->model.addItems(items); + return; + } else if (!item.extraInfo.isEmpty()) { + appendTag(item.extraInfo); + } + + d->referencePopup->hide(); + CodeGeeXManager::instance()->setRefereceFiles(d->selectedFiles); +} + +// use to restore tag, : remove tag then Ctrl+z +void InputEditWidget::onTagAdded(const QString &text) +{ + if (!d->tagMap.contains(text)) + return; + d->selectedFiles.append(d->tagMap[text]); + CodeGeeXManager::instance()->setRefereceFiles(d->selectedFiles); +} + +void InputEditWidget::onTagRemoved(const QString &text) +{ + if (!d->tagMap.contains(text)) + return; + for (auto item : d->tagMap[text]) + d->selectedFiles.removeOne(item); + CodeGeeXManager::instance()->setRefereceFiles(d->selectedFiles); +} + +#include "inputeditwidget.moc" diff --git a/src/plugins/codegeex/widgets/inputeditwidget.h b/src/plugins/codegeex/widgets/inputeditwidget.h new file mode 100644 index 000000000..a29248bb9 --- /dev/null +++ b/src/plugins/codegeex/widgets/inputeditwidget.h @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef INPUTEDITWIDGET_H +#define INPUTEDITWIDGET_H + +#include +#include + +#include + +class TagObjectInterface; +class TagTextFormat : public QTextCharFormat +{ +public: + TagTextFormat(); + void setText(const QString &text); + QString text() const; + +protected: + explicit TagTextFormat(const QTextFormat &fmt); + friend class InputEdit; + friend class TagObjectInterface; +}; + +class InputEdit : public DTK_WIDGET_NAMESPACE::DTextEdit +{ + Q_OBJECT +public: + explicit InputEdit(QWidget *parent = nullptr); + QString toPlainText() const; + void appendTag(const QString &text); + +signals: + void pressedEnter(); + void enterReference(); + void tagAdded(const QString &text); + void tagRemoved(const QString &text); + void focusOut(); + +public slots: + void onTextChanged(); + +protected: + bool event(QEvent *e) override; + void keyPressEvent(QKeyEvent *e) override; + void focusOutEvent(QFocusEvent *e) override; + +private: + QStringList formatList; + QMap formats; +}; + +class InputEditWidgetPrivate; +class InputEditWidget : public DTK_WIDGET_NAMESPACE::DFrame +{ + Q_OBJECT +public: + explicit InputEditWidget(QWidget *parent); + QTextEdit *edit(); + + void popupReference(); + void accept(const QModelIndex &index); +signals: + void pressedEnter(); + void messageSended(); + void handleKey(QKeyEvent *keyEvent); + +public slots: + void onReferenceBtnClicked(); + void onNetWorkBtnClicked(); + void onTagAdded(const QString &text); + void onTagRemoved(const QString &text); + +protected: + bool event(QEvent *e) override; + bool eventFilter(QObject *watched, QEvent *event) override; + + InputEditWidgetPrivate *d; +}; + +#endif // INPUTEDITWIDGET_H diff --git a/src/plugins/codegeex/widgets/messagecomponent.cpp b/src/plugins/codegeex/widgets/messagecomponent.cpp index b1587f70b..6d14b7158 100644 --- a/src/plugins/codegeex/widgets/messagecomponent.cpp +++ b/src/plugins/codegeex/widgets/messagecomponent.cpp @@ -183,34 +183,35 @@ void MessageComponent::initConnect() void MessageComponent::waitForAnswer() { waitingAnswer = true; - auto hlayout = new QHBoxLayout; - spinner = new DSpinner(this); + auto spinner = new DSpinner(this); spinner->setFixedSize(14, 14); - hlayout->addWidget(spinner); - hlayout->setAlignment(Qt::AlignLeft); - if (!searchingText) - searchingText = new DLabel(this); - searchingText->setWordWrap(true); + searchingWidget = new DWidget(this); + auto hlayout = new QHBoxLayout(searchingWidget); + auto searchingIcon = new DLabel(searchingWidget); + searchingIcon->setPixmap(QIcon::fromTheme("codegeex_internet").pixmap(QSize(14, 14))); + auto searchingText = new DLabel(tr("Online Searching"), searchingWidget); + + hlayout->setContentsMargins(0, 0, 0, 0); + hlayout->setAlignment(Qt::AlignLeft); + hlayout->addWidget(searchingIcon); hlayout->addWidget(searchingText); - msgLayout->addLayout(hlayout); + hlayout->addWidget(spinner); + msgLayout->addWidget(searchingWidget); + isConnecting = CodeGeeXManager::instance()->isConnectToNetWork(); + if (!isConnecting) { + searchingIcon->hide(); + searchingText->hide(); + } spinner->start(); - - connect(CodeGeeXManager::instance(), &CodeGeeXManager::searching, searchingText, [=](const QString &searchText) { - if (finished) - return; - isConnecting = true; - auto text = tr("online searching --- %1 ").arg(searchText); - searchingText->setText(text); - }); } void MessageComponent::stopWaiting() { if (waitingAnswer) { - msgLayout->removeWidget(spinner); - searchingText->hide(); - delete spinner; + msgLayout->removeWidget(searchingWidget); + searchingWidget->hide(); + delete searchingWidget; waitingAnswer = false; } } @@ -271,11 +272,12 @@ void MessageComponent::showWebsitesRefrences() auto separator = new QHBoxLayout; separator->setContentsMargins(0, 0, 0, 0); - separator->setSpacing(0); - auto toggleBtn = new DCommandLinkButton(tr("References"), this); - separator->addWidget(toggleBtn); - separator->addWidget(new DHorizontalLine); + auto toggleBtn = new DPushButton(this); + toggleBtn->setText(tr("Show Reference")); + toggleBtn->setFlat(true); + toggleBtn->setIcon(QIcon::fromTheme("codegeex_internet")); + separator->addWidget(toggleBtn); msgLayout->addLayout(separator); int count = 0; @@ -298,7 +300,7 @@ void MessageComponent::showWebsitesRefrences() if (!QDesktopServices::openUrl(website.url)) qWarning() << "can not open url: " << website.url; }); - connect(toggleBtn, &DCommandLinkButton::clicked, this, [=](){ + connect(toggleBtn, &DPushButton::clicked, this, [=]() { if (view->isVisible()) { msgLayout->removeWidget(view); view->hide(); diff --git a/src/plugins/codegeex/widgets/messagecomponent.h b/src/plugins/codegeex/widgets/messagecomponent.h index 097869841..5dba02dd0 100644 --- a/src/plugins/codegeex/widgets/messagecomponent.h +++ b/src/plugins/codegeex/widgets/messagecomponent.h @@ -56,8 +56,7 @@ class MessageComponent : public DFrame DLabel *senderName { nullptr }; DLabel *context { nullptr }; DPushButton *editButton { nullptr }; - DSpinner *spinner { nullptr }; - DLabel *searchingText { nullptr }; + DWidget *searchingWidget { nullptr }; QVBoxLayout *msgLayout { nullptr }; diff --git a/src/plugins/codegeex/widgets/referencepopup.cpp b/src/plugins/codegeex/widgets/referencepopup.cpp new file mode 100644 index 000000000..04955b43d --- /dev/null +++ b/src/plugins/codegeex/widgets/referencepopup.cpp @@ -0,0 +1,310 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "referencepopup.h" +#include "base/baseitemdelegate.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DTKWIDGET_CLASS_DPaletteHelper +# include +#endif +#include + +inline constexpr int kRectRadius = { 8 }; +inline constexpr int kTextLeftMargin = { 8 }; + +DisplayItemDelegate::DisplayItemDelegate(QAbstractItemView *parent) + : DStyledItemDelegate(parent) +{ +} + +void DisplayItemDelegate::paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + if (!index.isValid()) + return DStyledItemDelegate::paint(painter, option, index); + + painter->setRenderHints(painter->renderHints() + | QPainter::Antialiasing + | QPainter::TextAntialiasing + | QPainter::SmoothPixmapTransform); + + QStyleOptionViewItem opt = option; + initStyleOption(&opt, index); + + paintItemBackground(painter, opt, index); + paintItemColumn(painter, opt, index); +} + +QSize DisplayItemDelegate::sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + if (index.isValid()) { + QSize size = index.data(Qt::SizeHintRole).toSize(); + if (size.isValid()) { + return size; + } else { + return { option.rect.width(), option.fontMetrics.height() * 2 + 5 }; + } + } + return DStyledItemDelegate::sizeHint(option, index); +} + +void DisplayItemDelegate::paintItemBackground(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + painter->save(); + + if (option.widget) { +#ifdef DTKWIDGET_CLASS_DPaletteHelper + DPalette pl(DPaletteHelper::instance()->palette(option.widget)); +#else + DPalette pl = DGuiApplicationHelper::instance()->applicationPalette(); +#endif + QColor baseColor = pl.color(DPalette::ColorGroup::Active, DPalette::ColorType::ItemBackground); + QColor adjustColor = baseColor; + + bool isSelected = (option.state & QStyle::State_Selected) && option.showDecorationSelected; + if (isSelected) { + adjustColor = option.palette.color(DPalette::ColorGroup::Active, QPalette::Highlight); + } else if (option.state & QStyle::StateFlag::State_MouseOver) { + // hover color + adjustColor = DGuiApplicationHelper::adjustColor(baseColor, 0, 0, 0, 0, 0, 0, +10); + } else { + painter->setOpacity(0); + } + + // set paint path + QPainterPath path; + path.addRoundedRect(option.rect, kRectRadius, kRectRadius); + painter->fillPath(path, adjustColor); + } + + painter->restore(); +} + +void DisplayItemDelegate::paintItemColumn(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + painter->save(); + + bool isSelected = (option.state & QStyle::State_Selected) && option.showDecorationSelected; + if (isSelected) + painter->setPen(option.palette.color(DPalette::ColorGroup::Active, QPalette::HighlightedText)); + + auto model = dynamic_cast(index.model()); + if (!model) + return; + auto items = model->getItems(); + auto itemInfo = items.at(index.row()); + QString filePath = itemInfo.extraInfo; + QString fileName = itemInfo.displayName; + QFontMetrics fm(option.font); + + QString elidFilePath; + elidFilePath = fm.elidedText(filePath, Qt::ElideMiddle, option.rect.width()); + + painter->drawText(option.rect.adjusted(kTextLeftMargin, 5, 0, -5), + Qt::AlignLeft | Qt::AlignBottom, elidFilePath); + + QString elidFileName; + elidFileName = fm.elidedText(fileName, Qt::ElideMiddle, option.rect.width()); + painter->drawText(option.rect.adjusted(kTextLeftMargin, 5, 0, -5), + Qt::AlignLeft | Qt::AlignTop, elidFileName); + + painter->restore(); +} + +void ItemModel::clear() +{ + beginResetModel(); + items.clear(); + endResetModel(); +} + +QVariant ItemModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() >= items.size()) + return QVariant(); + + switch (role) { + case Qt::DisplayRole: + if (index.column() == DisplayNameColumn) + return items.at(index.row()).displayName; + else if (index.column() == ExtraInfoColumn) + return items.at(index.row()).extraInfo; + break; + case Qt::ToolTipRole: + if (!items.at(index.row()).extraInfo.isEmpty()) + return items.at(index.row()).extraInfo; + case Qt::DecorationRole: + if (index.column() == DisplayNameColumn) + return items[index.row()].icon; + break; + }; + return QVariant(); +} + +void ItemModel::addItems(const QList &items) +{ + beginInsertRows(QModelIndex(), this->items.size(), this->items.size() + items.size() - 1); + this->items.append(items); + endInsertRows(); +} + +QList ItemModel::getItems() const +{ + return items; +} + +int ItemModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + return items.size(); +} + +int ItemModel::columnCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + for (auto item : items) + if (!item.extraInfo.isEmpty()) + return ColumnCount; + return 1; +} + +PopupWidget::PopupWidget(QWidget *parent) + : DBlurEffectWidget(parent), list(new DListView(this)) +{ + list->setFrameShape(QFrame::NoFrame); + list->setItemDelegate(new BaseItemDelegate(this)); + auto layout = new QVBoxLayout; + layout->setSizeConstraint(QLayout::SetMinimumSize); + setLayout(layout); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + layout->addWidget(list); + + list->setAutoFillBackground(false); + list->setSelectionMode(QAbstractItemView::SingleSelection); + list->setEditTriggers(QListView::NoEditTriggers); + list->setSizeAdjustPolicy(QAbstractItemView::AdjustToContents); + list->setAlternatingRowColors(false); + list->setSelectionRectVisible(true); + list->setUniformItemSizes(true); + list->setOrientation(QListView::TopToBottom, false); + list->setItemDelegate(new DisplayItemDelegate(list)); + + connect(list, &DListView::activated, this, [this](const QModelIndex &index) { + emit this->selectIndex(index); + }); + + updateGeometry(); +} + +void PopupWidget::updateGeometry() +{ + Q_ASSERT(parentWidget()); + + const int border = list->frameWidth(); + QSize size = parentWidget()->size(); + auto itemHeight = fontMetrics().height() * 2 + 5; // same as itemdelegate + auto height = 10 * itemHeight; + if (model && model->rowCount() < 10) + height = model->rowCount() * itemHeight; + + const QRect rect(parentWidget()->mapToGlobal(QPoint(-border, -(height + 5))), QSize(size.width(), height)); + setGeometry(rect); +} + +void PopupWidget::selectFirstRow() +{ + if (model && (model->rowCount(QModelIndex()) > 0)) + list->setCurrentIndex(model->index(0, 0)); +} + +void PopupWidget::previous() +{ + int index = list->currentIndex().row(); + --index; + if (index < 0) { + // wrap + index = model->rowCount(QModelIndex()) - 1; + } + list->setCurrentIndex(model->index(index, 0)); +} + +void PopupWidget::next() +{ + int index = list->currentIndex().row(); + ++index; + if (index >= model->rowCount(QModelIndex())) { + // wrap + index = 0; + } + list->setCurrentIndex(model->index(index, 0)); +} + +bool PopupWidget::event(QEvent *event) +{ + if (event->type() == QEvent::Show || event->type() == QEvent::LayoutRequest) + // make sure the popup has correct position before it becomes visible + updateGeometry(); + return QWidget::event(event); +} + +bool PopupWidget::eventFilter(QObject *watched, QEvent *event) +{ + if (event->type() == QEvent::Resize) + updateGeometry(); + return QWidget::eventFilter(watched, event); +} + +void PopupWidget::setmodel(ItemModel *model) +{ + list->setModel(model); + this->model = model; +} + +void PopupWidget::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Tab: + case Qt::Key_Down: + next(); + return; + case Qt::Key_Backtab: + case Qt::Key_Up: + previous(); + return; + case Qt::Key_P: + case Qt::Key_N: + if (event->modifiers() == Qt::Modifier::CTRL) { + if (event->key() == Qt::Key_P) + previous(); + else + next(); + return; + } + break; + case Qt::Key_Return: + case Qt::Key_Enter: + if (event->modifiers() == 0) { + emit selectIndex(list->currentIndex()); + return; + } + break; + } + DBlurEffectWidget::keyPressEvent(event); +} diff --git a/src/plugins/codegeex/widgets/referencepopup.h b/src/plugins/codegeex/widgets/referencepopup.h new file mode 100644 index 000000000..48d6b4720 --- /dev/null +++ b/src/plugins/codegeex/widgets/referencepopup.h @@ -0,0 +1,105 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef REFERENCESPOPUP_H +#define REFERENCESPOPUP_H + +#include +#include + +#include + +DWIDGET_USE_NAMESPACE + +class DisplayItemDelegate : public DStyledItemDelegate +{ +public: + static inline Qt::Alignment visualAlignment(Qt::LayoutDirection direction, Qt::Alignment alignment) + { + if (!(alignment & Qt::AlignHorizontal_Mask)) + alignment |= Qt::AlignLeft; + if (!(alignment & Qt::AlignAbsolute) && (alignment & (Qt::AlignLeft | Qt::AlignRight))) { + if (direction == Qt::RightToLeft) + alignment ^= (Qt::AlignLeft | Qt::AlignRight); + alignment |= Qt::AlignAbsolute; + } + return alignment; + } + static QPixmap getIconPixmap(const QIcon &icon, const QSize &size, qreal pixelRatio, + QIcon::Mode mode = QIcon::Normal, QIcon::State state = QIcon::Off); + + explicit DisplayItemDelegate(QAbstractItemView *parent = nullptr); + +protected: + virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const override; + + QSize sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const override; + + void paintItemBackground(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + void paintItemColumn(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const; +}; + +struct ItemInfo +{ + QString type; + QString displayName; + QString extraInfo; + QIcon icon; +}; + +class ItemModel : public QAbstractListModel +{ +public: + enum Columns { + DisplayNameColumn, + ExtraInfoColumn, + ColumnCount + }; + + ItemModel(QObject *parent = nullptr) + : QAbstractListModel(parent) {} + void clear(); + QVariant data(const QModelIndex &index, int role = Qt::DisplayPropertyRole) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + + void addItems(const QList &items); + + QList getItems() const; + +private: + QList items; +}; + +class PopupWidget : public DBlurEffectWidget +{ + Q_OBJECT +public: + PopupWidget(QWidget *parent = nullptr); + void setmodel(ItemModel *model); + void keyPressEvent(QKeyEvent *event) override; + void selectFirstRow(); + +signals: + void selectIndex(const QModelIndex &index); + +protected: + void updateGeometry(); + + void next(); + void previous(); + + bool event(QEvent *event) override; + bool eventFilter(QObject *watched, QEvent *event) override; + +private: + DListView *list { nullptr }; + ItemModel *model { nullptr }; +}; + +#endif // REFERENCESPOPUP_H diff --git a/src/plugins/recent/mainframe/displayitemdelegate.cpp b/src/plugins/recent/mainframe/displayitemdelegate.cpp index a3143f65c..36d1e434a 100644 --- a/src/plugins/recent/mainframe/displayitemdelegate.cpp +++ b/src/plugins/recent/mainframe/displayitemdelegate.cpp @@ -11,7 +11,7 @@ #include #include #ifdef DTKWIDGET_CLASS_DPaletteHelper -#include +# include #endif #include @@ -31,9 +31,8 @@ inline constexpr int kIconLeftMargin = { 10 }; inline constexpr int kTextLeftMargin = { 8 }; DisplayItemDelegate::DisplayItemDelegate(QAbstractItemView *parent) - : DStyledItemDelegate (parent) + : DStyledItemDelegate(parent) { - } void DisplayItemDelegate::paint(QPainter *painter, @@ -66,7 +65,7 @@ QSize DisplayItemDelegate::sizeHint(const QStyleOptionViewItem &option, if (size.isValid()) { return size; } else { - return {option.rect.width(), option.fontMetrics.height() * 2 + 5}; + return { option.rect.width(), option.fontMetrics.height() * 2 + 5 }; } } return DStyledItemDelegate::sizeHint(option, index); @@ -119,7 +118,6 @@ QRectF DisplayItemDelegate::paintItemIcon(QPainter *painter, const QStyleOptionV if (!parent() || !parent()->parent()) return QRect(); - // draw icon QRectF iconRect = option.rect; iconRect.setSize(QSize(kIconWidth, kIconHeight));