diff --git a/assets/translations/en_US.ts b/assets/translations/en_US.ts index 78e25f7f9..430d5dc1b 100644 --- a/assets/translations/en_US.ts +++ b/assets/translations/en_US.ts @@ -4123,52 +4123,62 @@ repos path: %0 ProjectTreeView - + Open File - - Generate UT + + Regenerate - + + Continue To Generate + + + + + Generate + + + + Stop - + Unignore - + Ignore - + Show Containing Folder - + Collapse - + Expand - + Collapse All - + Expand All @@ -6733,37 +6743,32 @@ not exists support files: %0 SmartUTWidget - + The current resource is not configured - + <html><head/><body><p>Please click the Setting button "%1" in the upper right corner to configure</p></body></html> - + Generate unit test files - - Run - - - - - Generate coverage report + + Continue to generate - + Stop - + Select Model: @@ -7010,7 +7015,7 @@ not exists support files: %0 TaskDelegate - + File not found: %1 @@ -7018,13 +7023,13 @@ not exists support files: %0 TaskManager - + Clear - - Fix Issue + + Smart Analysis diff --git a/assets/translations/zh_CN.ts b/assets/translations/zh_CN.ts index f93897cf5..028aede15 100644 --- a/assets/translations/zh_CN.ts +++ b/assets/translations/zh_CN.ts @@ -4160,52 +4160,62 @@ repos path: %0 ProjectTreeView - + Open File 打开文件 - - Generate UT - 生成单元测试 + + Regenerate + 重新生成 + + + + Continue To Generate + 继续生成 + + + + Generate + 生成 - + Stop 停止 - + Unignore 取消忽略 - + Ignore 忽略 - + Show Containing Folder 显示所在文件夹 - + Collapse 折叠 - + Expand 展开 - + Collapse All 折叠所有 - + Expand All 展开所有 @@ -6779,37 +6789,32 @@ not exists support files: %0 SmartUTWidget - + The current resource is not configured 当前资源信息未配置 - + <html><head/><body><p>Please click the Setting button "%1" in the upper right corner to configure</p></body></html> <html><head/><body><p>请点击右上角的的设置按钮“%1”进行配置</p></body></html> - + Generate unit test files 生成单元测试文件 - - Run - 运行 - - - - Generate coverage report - 生成覆盖率报告 + + Continue to generate + 继续生成 - + Stop 停止 - + Select Model: 选择模型: @@ -7056,7 +7061,7 @@ not exists support files: %0 TaskDelegate - + File not found: %1 找不到文件:%1 @@ -7064,14 +7069,14 @@ not exists support files: %0 TaskManager - + Clear 清除 - - Fix Issue - 修复问题 + + Smart Analysis + 智能分析 diff --git a/src/plugins/aimanager/openai/openaicompatiblellm.cpp b/src/plugins/aimanager/openai/openaicompatiblellm.cpp index deb36265b..9acf870fb 100644 --- a/src/plugins/aimanager/openai/openaicompatiblellm.cpp +++ b/src/plugins/aimanager/openai/openaicompatiblellm.cpp @@ -53,7 +53,7 @@ class OpenAiCompatibleLLMPrivate QNetworkReply *getMessage(const QString &url, const QString &apiKey); void replyMessage(const QString &data, AbstractLLM::ResponseState state, AbstractLLM::ResponseHandler handler); void processResponse(QNetworkReply *reply, AbstractLLM::ResponseHandler handler = nullptr); - void handleReplyFinished(QNetworkReply *reply); + void handleReplyFinished(QNetworkReply *reply, AbstractLLM::ResponseHandler handler = nullptr); QString modelName { "" }; QString modelPath { "" }; @@ -161,13 +161,13 @@ void OpenAiCompatibleLLMPrivate::processResponse(QNetworkReply *reply, AbstractL }); } -void OpenAiCompatibleLLMPrivate::handleReplyFinished(QNetworkReply *reply) +void OpenAiCompatibleLLMPrivate::handleReplyFinished(QNetworkReply *reply, AbstractLLM::ResponseHandler handler) { if (q->modelState() == AbstractLLM::Idle) // llm is alread stopped return; if (reply->error()) { qWarning() << "NetWork Error: " << reply->errorString(); - emit q->dataReceived(reply->errorString(), AbstractLLM::ResponseState::Failed); + replyMessage(reply->errorString(), AbstractLLM::Failed, handler); } q->setModelState(AbstractLLM::Idle); } @@ -296,7 +296,7 @@ void OpenAiCompatibleLLM::request(const QString &prompt, ResponseHandler handler QNetworkReply *reply = d->postMessage(modelPath() + "/completions", d->apiKey, QJsonDocument(dataObject).toJson()); connect(this, &OpenAiCompatibleLLM::requstCancel, reply, &QNetworkReply::abort); connect(reply, &QNetworkReply::finished, this, [=](){ - d->handleReplyFinished(reply); + d->handleReplyFinished(reply, handler); }); d->processResponse(reply, handler); diff --git a/src/plugins/builder/builder.qrc b/src/plugins/builder/builder.qrc index 4877ee361..9edcd59a9 100644 --- a/src/plugins/builder/builder.qrc +++ b/src/plugins/builder/builder.qrc @@ -6,5 +6,6 @@ texts/rebuild_16px.svg texts/filter_16px.svg texts/clear_log_16px.svg + texts/uc_repair_16px.svg diff --git a/src/plugins/builder/mainframe/settingdialog.cpp b/src/plugins/builder/mainframe/settingdialog.cpp index 6cf4803a7..7b2b7f465 100644 --- a/src/plugins/builder/mainframe/settingdialog.cpp +++ b/src/plugins/builder/mainframe/settingdialog.cpp @@ -193,6 +193,5 @@ bool SettingDialog::eventFilter(QObject *obj, QEvent *e) QString SettingDialog::defaultIssueFixPrompt() { - return "How can I resolve this? If you propose a fix, please make it concise." - "For the code present, we get this error:"; + return "如何解决这个问题?如果你提出修复方案,请尽量简洁。对于当前代码,我们遇到以下错误:"; } diff --git a/src/plugins/builder/tasks/taskdelegate.cpp b/src/plugins/builder/tasks/taskdelegate.cpp index 4c6471485..5a28fe338 100644 --- a/src/plugins/builder/tasks/taskdelegate.cpp +++ b/src/plugins/builder/tasks/taskdelegate.cpp @@ -3,6 +3,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "taskdelegate.h" +#include "taskmodel.h" #include #ifdef DTKWIDGET_CLASS_DPaletteHelper @@ -14,14 +15,20 @@ #include #include #include +#include DWIDGET_USE_NAMESPACE -inline constexpr int kRectRadius = { 8 }; +inline constexpr int kRectRadius { 8 }; +inline constexpr int kTaskIconSize { 16 }; +inline constexpr int kItemMargin { 10 }; +inline constexpr int kItemMinHeight { 24 }; -TaskDelegate::TaskDelegate(QAbstractItemView *parent) : - DStyledItemDelegate(parent) -{ } +TaskDelegate::TaskDelegate(TaskView *parent) + : DStyledItemDelegate(parent), + view(parent) +{ +} TaskDelegate::~TaskDelegate() = default; @@ -29,29 +36,24 @@ QSize TaskDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelInd { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); + opt.rect.adjust(8, 0, -8, 0); auto view = qobject_cast(opt.widget); const bool selected = (view->selectionModel()->currentIndex() == index); QSize s; - s.setWidth(option.rect.width()); - - if (!selected && option.font == cachedFont && cachedHeight > 0) { - s.setHeight(cachedHeight); - return s; - } - - QFontMetrics fm(option.font); - int fontHeight = fm.height(); - int fontLeading = fm.leading(); - - auto model = static_cast(view->model()); - Positions positions(option, model); + s.setWidth(opt.rect.width()); if (selected) { + QFontMetrics fm(option.font); + int fontHeight = fm.height(); + int fontLeading = fm.leading(); QString description = index.data(TaskModel::Description).toString(); // Layout the description int leading = fontLeading; int height = 0; + int textWidth = fixButtonRect(opt.rect).left() + - iconRect(opt.rect).right() + - 2 * kItemMargin + 1; description.replace(QLatin1Char('\n'), QChar::LineSeparator); QTextLayout tl(description); @@ -60,7 +62,7 @@ QSize TaskDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelInd QTextLine line = tl.createLine(); if (!line.isValid()) break; - line.setLineWidth(positions.textAreaWidth()); + line.setLineWidth(textWidth); height += leading; line.setPosition(QPoint(0, height)); height += static_cast(line.height()); @@ -68,20 +70,36 @@ QSize TaskDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelInd tl.endLayout(); s.setHeight(height + leading + fontHeight + 3); - } else { - s.setHeight(fontHeight + 3); } - if (s.height() < positions.minimumHeight()) - s.setHeight(positions.minimumHeight()); - if (!selected) { - cachedHeight = s.height(); - cachedFont = option.font; - } + if (s.height() < kItemMinHeight) + s.setHeight(kItemMinHeight); return s; } +bool TaskDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) +{ + if (event->type() == QEvent::MouseMove || event->type() == QEvent::MouseButtonPress) { + Q_EMIT model->layoutChanged({ QPersistentModelIndex(index) }); + return false; + } else if (event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::MouseButtonDblClick) { + const QMouseEvent *mouseEvent = static_cast(event); + if (mouseEvent->button() == Qt::LeftButton) { + QPoint mousePos = option.widget->mapFromGlobal(QCursor::pos()); + auto itemRect = option.rect.adjusted(8, 0, -8, 0); + const auto &btnRect = fixButtonRect(itemRect); + if (btnRect.contains(mousePos)) { + Q_EMIT model->layoutChanged({ QPersistentModelIndex(index) }); + Q_EMIT view->sigFixIssue(index); + return true; + } + } + } + + return false; +} + void TaskDelegate::emitSizeHintChanged(const QModelIndex &index) { emit sizeHintChanged(index); @@ -105,29 +123,18 @@ void TaskDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, QStyleOptionViewItem opt = option; initStyleOption(&opt, index); + opt.rect.adjust(8, 0, -8, 0); paintItemBackground(painter, opt, index); painter->setOpacity(1); - auto view = qobject_cast(opt.widget); - bool selected = view->selectionModel()->currentIndex() == index; - auto model = static_cast(view->model()); - Positions positions(opt, model); - - // Paint TaskIconArea: - QPoint iconPos; - - iconPos = QPoint(positions.left() + 10, - positions.getTop() + (positions.getBottom() - positions.getTop() - positions.taskIconHeight()) / 2 ); - QIcon icon = index.data(TaskModel::Icon).value(); - painter->drawPixmap(iconPos, icon.pixmap(positions.taskIconWidth(), positions.taskIconHeight())); - - QRect textRect = option.rect.adjusted(30, 0, 6, 0); - paintItemColumn(painter, opt, index, textRect); + QRect icRect = iconRect(opt.rect); + painter->drawPixmap(icRect, icon.pixmap(kTaskIconSize, kTaskIconSize)); + paintItemColumn(painter, opt, index, icRect); } void TaskDelegate::paintItemBackground(QPainter *painter, const QStyleOptionViewItem &option, - const QModelIndex &index) const + const QModelIndex &index) const { painter->save(); @@ -166,22 +173,27 @@ void TaskDelegate::paintItemBackground(QPainter *painter, const QStyleOptionView } void TaskDelegate::paintItemColumn(QPainter *painter, const QStyleOptionViewItem &option, - const QModelIndex &index, const QRectF &textRect) const + const QModelIndex &index, const QRect &iconRect) const { - painter->save(); - + auto btnRect = paintFixButton(painter, option, index); bool isSelected = (option.state & QStyle::State_Selected) && option.showDecorationSelected; - if(DGuiApplicationHelper::instance()->themeType() == DGuiApplicationHelper::ColorType::DarkType){ + painter->save(); + if (DGuiApplicationHelper::instance()->themeType() == DGuiApplicationHelper::ColorType::DarkType) { painter->setPen(QColor(255, 255, 255, 180)); } else { painter->setPen(QColor(0, 0, 0, 180)); } + QRect textRect = option.rect; + textRect.setLeft(iconRect.right() + kItemMargin); + textRect.setRight(btnRect.left() - kItemMargin); + QString description = index.data(TaskModel::Description).toString(); - if (!isSelected) + if (!isSelected) { + description = option.fontMetrics.elidedText(description, Qt::ElideRight, textRect.width()); painter->drawText(textRect, Qt::AlignLeft, description); - else { + } else { QFontMetrics fm(option.font); description.replace(QLatin1Char('\n'), QChar::LineSeparator); painter->setPen(QColor(Qt::white)); @@ -203,18 +215,92 @@ void TaskDelegate::paintItemColumn(QPainter *painter, const QStyleOptionViewItem tl.draw(painter, textRect.topLeft()); } - const QString directory = QDir::toNativeSeparators(index.data(TaskModel::File).toString()); - + QString directory = QDir::toNativeSeparators(index.data(TaskModel::File).toString()); if (isSelected) { painter->setPen(QColor(Qt::white)); if (index.data(TaskModel::FileNotFound).toBool() && !directory.isEmpty()) { QString fileNotFound = tr("File not found: %1").arg(directory); + fileNotFound = option.fontMetrics.elidedText(fileNotFound, Qt::ElideRight, textRect.width()); painter->setPen(Qt::red); painter->drawText(textRect, Qt::AlignLeft | Qt::AlignBottom, fileNotFound); } else { + directory = option.fontMetrics.elidedText(directory, Qt::ElideRight, textRect.width()); painter->drawText(textRect, Qt::AlignLeft | Qt::AlignBottom, directory); } } painter->restore(); } + +QRect TaskDelegate::paintFixButton(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + auto btnRect = fixButtonRect(option.rect); + if (!option.state.testFlag(QStyle::State_MouseOver)) + return btnRect; + + QPoint mousePos = view->mapFromGlobal(QCursor::pos()); + if (btnRect.contains(mousePos)) { + if (QApplication::mouseButtons() & Qt::LeftButton) { + QColor bgColor(255, 255, 255, qRound(255 * 0.15)); + painter->save(); + painter->setPen(Qt::NoPen); + painter->setBrush(bgColor); + painter->drawRoundedRect(btnRect, 6, 6); + painter->restore(); + } else if (option.state.testFlag(QStyle::State_MouseOver)) { + QColor bgColor(0, 0, 0, qRound(255 * 0.08)); + if (option.state.testFlag(QStyle::State_Selected)) { + bgColor.setRgba(qRgba(255, 255, 255, qRound(255 * 0.2))); + } else if (DGuiApplicationHelper::instance()->themeType() == DGuiApplicationHelper::DarkType) { + bgColor.setRgba(qRgba(255, 255, 255, qRound(255 * 0.08))); + } + painter->save(); + painter->setPen(Qt::NoPen); + painter->setBrush(bgColor); + painter->drawRoundedRect(btnRect, 6, 6); + painter->restore(); + } + } + + QIcon::Mode iconMode = QIcon::Normal; + if (!(option.state.testFlag(QStyle::State_Enabled))) + iconMode = QIcon::Disabled; + if (option.state.testFlag(QStyle::State_Selected)) + iconMode = QIcon::Selected; + + auto icon = QIcon::fromTheme("uc_repair"); + auto px = icon.pixmap({ kTaskIconSize, kTaskIconSize }, iconMode); + px.setDevicePixelRatio(qApp->devicePixelRatio()); + qreal x = btnRect.x(); + qreal y = btnRect.y(); + qreal w = px.width() / px.devicePixelRatio(); + qreal h = px.height() / px.devicePixelRatio(); + y += (btnRect.size().height() - h) / 2.0; + x += (btnRect.size().width() - w) / 2.0; + + painter->drawPixmap(qRound(x), qRound(y), px); + return btnRect; +} + +QRect TaskDelegate::iconRect(const QRect &itemRect) const +{ + QRect iconRect = itemRect; + iconRect.setSize({ kTaskIconSize, kTaskIconSize }); + + iconRect.moveLeft(iconRect.left() + kItemMargin); + iconRect.moveTop(iconRect.top() + ((itemRect.bottom() - iconRect.bottom()) / 2)); + + return iconRect; +} + +QRect TaskDelegate::fixButtonRect(const QRect &itemRect) const +{ + QRect btnRect = itemRect; + + btnRect.setSize({ 20, 20 }); + btnRect.moveRight(itemRect.right() - kItemMargin); + btnRect.moveTop(btnRect.top() + ((itemRect.bottom() - btnRect.bottom()) / 2)); + + return btnRect; +} diff --git a/src/plugins/builder/tasks/taskdelegate.h b/src/plugins/builder/tasks/taskdelegate.h index ace120fcf..690749a71 100644 --- a/src/plugins/builder/tasks/taskdelegate.h +++ b/src/plugins/builder/tasks/taskdelegate.h @@ -5,7 +5,7 @@ #ifndef TASKDELEGATE_H #define TASKDELEGATE_H -#include "taskfilterproxymodel.h" +#include "taskview.h" #include @@ -16,14 +16,12 @@ class Positions; class TaskDelegate : public DTK_WIDGET_NAMESPACE::DStyledItemDelegate { Q_OBJECT - - friend class TaskView; // for using Positions::minimumSize() - public: - explicit TaskDelegate(QAbstractItemView *parent); + explicit TaskDelegate(TaskView *parent); ~TaskDelegate() override; void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; + bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override; // TaskView uses this method if the size of the taskview changes void emitSizeHintChanged(const QModelIndex &index); @@ -36,62 +34,14 @@ class TaskDelegate : public DTK_WIDGET_NAMESPACE::DStyledItemDelegate void paintItemBackground(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; void paintItemColumn(QPainter *painter, const QStyleOptionViewItem &option, - const QModelIndex &index, const QRectF &textRect) const; - - mutable int cachedHeight = 0; - mutable QFont cachedFont; - - class Positions - { - public: - Positions(const QStyleOptionViewItem &options, TaskFilterProxyModel *model) : - totalWidth(options.rect.width()), - realFileLength(maxFileLength), - top(options.rect.top()), - bottom(options.rect.bottom()) - { - TaskModel *sourceModel = static_cast(model->sourceModel()); - if (sourceModel) { - maxFileLength = sourceModel->sizeOfFile(options.font); - maxLineLength = sourceModel->getSizeOfLineNumber(options.font); - } else { - maxFileLength = 0; - maxLineLength = 0; - } - fontHeight = QFontMetrics(options.font).height(); - } - - int getTop() const { return top + ITEM_MARGIN; } - int left() const { return ITEM_MARGIN; } - int right() const { return totalWidth - ITEM_MARGIN; } - int getBottom() const { return bottom; } - int firstLineHeight() const { return fontHeight + 1; } - static int minimumHeight() { return taskIconHeight() + 2 * ITEM_MARGIN; } - - int taskIconLeft() const { return left(); } - static int taskIconWidth() { return TASK_ICON_SIZE; } - static int taskIconHeight() { return TASK_ICON_SIZE; } - int taskIconRight() const { return taskIconLeft() + taskIconWidth(); } - QRect taskIcon() const { return QRect(taskIconLeft(), getTop(), taskIconWidth(), taskIconHeight()); } - - int textAreaLeft() const { return taskIconRight() + ITEM_SPACING; } - int textAreaWidth() const { return textAreaRight() - textAreaLeft(); } - int textAreaRight() const { return right(); } - QRect textArea() const { return QRect(textAreaLeft(), getTop(), textAreaWidth(), firstLineHeight()); } + const QModelIndex &index, const QRect &iconRect) const; + QRect paintFixButton(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const; - private: - int totalWidth = 0; - int maxFileLength = 0; - int maxLineLength = 0; - int realFileLength = 0; - int top = 0; - int bottom = 0; - int fontHeight = 0; + QRect iconRect(const QRect &itemRect) const; + QRect fixButtonRect(const QRect &itemRect) const; - static const int TASK_ICON_SIZE = 16; - static const int ITEM_MARGIN = 2; - static const int ITEM_SPACING = 2 * ITEM_MARGIN; - }; + TaskView *view { nullptr }; }; -#endif // TASKDELEGATE_H +#endif // TASKDELEGATE_H diff --git a/src/plugins/builder/tasks/taskmanager.cpp b/src/plugins/builder/tasks/taskmanager.cpp index 36ed6b39f..36a9f76f9 100644 --- a/src/plugins/builder/tasks/taskmanager.cpp +++ b/src/plugins/builder/tasks/taskmanager.cpp @@ -8,6 +8,7 @@ #include "common/common.h" #include "services/option/optionmanager.h" #include "services/ai/aiservice.h" +#include "services/window/windowservice.h" #include @@ -32,6 +33,9 @@ void TaskManager::clearTasks() TaskManager::TaskManager(QObject *parent) : QObject(parent) { + aiSrv = dpfGetService(AiService); + winSrv = dpfGetService(WindowService); + view = new TaskView(); model.reset(new TaskModel()); filterModel.reset(new TaskFilterProxyModel()); @@ -53,6 +57,8 @@ TaskManager::TaskManager(QObject *parent) this, &TaskManager::triggerDefaultHandler); connect(view, &TaskView::customContextMenuRequested, this, &TaskManager::showContextMenu); + connect(view, &TaskView::sigFixIssue, + this, &TaskManager::fixIssueWithAi); } QString TaskManager::readContext(const QString &path, int codeLine) @@ -96,22 +102,22 @@ void TaskManager::showSpecificTasks(ShowType type) filterModel->setFilterType(type); } -void TaskManager::showContextMenu() +void TaskManager::showContextMenu(const QPoint &pos) { QMenu menu; menu.addAction(tr("Clear"), this, &TaskManager::clearTasks); - auto act = menu.addAction(tr("Fix Issue"), this, &TaskManager::fixIssueWithAi); - auto pos = QCursor::pos(); - if (!view->indexAt(view->mapFromGlobal(pos)).isValid()) + auto index = view->indexAt(pos); + auto act = menu.addAction(tr("Smart Analysis"), this, std::bind(&TaskManager::fixIssueWithAi, this, index)); + if (!index.isValid()) act->setEnabled(false); - menu.exec(pos); + menu.exec(QCursor::pos()); } -void TaskManager::fixIssueWithAi() +void TaskManager::fixIssueWithAi(const QModelIndex &index) { - auto realIndex = filterModel->mapToSource(view->currentIndex()); + auto realIndex = filterModel->mapToSource(index); const auto &task = model->task(realIndex); if (task.isNull()) return; @@ -130,9 +136,8 @@ void TaskManager::fixIssueWithAi() } prompt += task.description; - if (!aiSrv) - aiSrv = dpfGetService(AiService); aiSrv->chatWithAi(prompt); + winSrv->showWidgetAtRightspace(MWNA_CODEGEEX); } void TaskManager::currentChanged(const QModelIndex &index) diff --git a/src/plugins/builder/tasks/taskmanager.h b/src/plugins/builder/tasks/taskmanager.h index 7b3205266..fa08a3ef2 100644 --- a/src/plugins/builder/tasks/taskmanager.h +++ b/src/plugins/builder/tasks/taskmanager.h @@ -12,6 +12,7 @@ namespace dpfservice { class AiService; +class WindowService; } /** @@ -37,8 +38,8 @@ public slots: void triggerDefaultHandler(const QModelIndex &index); void showSpecificTasks(ShowType type); - void showContextMenu(); - void fixIssueWithAi(); + void showContextMenu(const QPoint &pos); + void fixIssueWithAi(const QModelIndex &index); private: explicit TaskManager(QObject *parent = nullptr); @@ -49,6 +50,7 @@ public slots: QSharedPointer model; QSharedPointer filterModel; dpfservice::AiService *aiSrv = nullptr; + dpfservice::WindowService *winSrv = nullptr; }; #endif // TASKMANAGER_H diff --git a/src/plugins/builder/tasks/taskview.cpp b/src/plugins/builder/tasks/taskview.cpp index adb4380ce..69287fea0 100644 --- a/src/plugins/builder/tasks/taskview.cpp +++ b/src/plugins/builder/tasks/taskview.cpp @@ -12,13 +12,7 @@ TaskView::TaskView(QWidget *parent) : DListView(parent) setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); setAutoFillBackground(true); - - QFontMetrics fm(font()); - int vStepSize = fm.height() + 3; - if (vStepSize < TaskDelegate::Positions::minimumHeight()) - vStepSize = TaskDelegate::Positions::minimumHeight(); - - verticalScrollBar()->setSingleStep(vStepSize); + setMouseTracking(true); } TaskView::~TaskView() = default; diff --git a/src/plugins/builder/tasks/taskview.h b/src/plugins/builder/tasks/taskview.h index 8fa9684ee..aa8ce54c5 100644 --- a/src/plugins/builder/tasks/taskview.h +++ b/src/plugins/builder/tasks/taskview.h @@ -14,6 +14,9 @@ class TaskView : public DTK_WIDGET_NAMESPACE::DListView explicit TaskView(QWidget *parent = nullptr); ~TaskView() override; void resizeEvent(QResizeEvent *e) override; + +signals: + void sigFixIssue(const QModelIndex &index); }; -#endif // TASKVIEW_H +#endif // TASKVIEW_H diff --git a/src/plugins/builder/texts/uc_repair_16px.svg b/src/plugins/builder/texts/uc_repair_16px.svg new file mode 100644 index 000000000..282e367dd --- /dev/null +++ b/src/plugins/builder/texts/uc_repair_16px.svg @@ -0,0 +1,7 @@ + + + ICON / ICON / AIrepair + + + + \ No newline at end of file diff --git a/src/plugins/smartut/gui/projecttreeview.cpp b/src/plugins/smartut/gui/projecttreeview.cpp index 1a7dd8079..6c7b6aa02 100644 --- a/src/plugins/smartut/gui/projecttreeview.cpp +++ b/src/plugins/smartut/gui/projecttreeview.cpp @@ -26,6 +26,7 @@ class ProjectTreeViewPrivate : public QObject void initConnecttion(); void setItemIgnoreState(NodeItem *item, bool ignore); + void createUTActoins(QMenu *menu, NodeItem *item); public: ProjectTreeView *q; @@ -84,6 +85,40 @@ void ProjectTreeViewPrivate::setItemIgnoreState(NodeItem *item, bool ignore) } } +void ProjectTreeViewPrivate::createUTActoins(QMenu *menu, NodeItem *item) +{ + if (item->itemNode->isFileNodeType()) { + auto act = menu->addAction(ProjectTreeView::tr("Open File"), this, [item] { + editor.openFile(QString(), item->itemNode->filePath()); + }); + act->setEnabled(QFile::exists(item->itemNode->filePath())); + } + + bool allIgnored = Utils::checkAllState(item, Ignored); + QAction *act = nullptr; + if (Utils::checkAllState(item, Completed)) + act = menu->addAction(ProjectTreeView::tr("Regenerate"), this, std::bind(&ProjectTreeView::reqGenerateUTFile, q, item)); + else if (!item->itemNode->isFileNodeType() && Utils::checkAnyState(item, Completed)) + act = menu->addAction(ProjectTreeView::tr("Continue To Generate"), this, std::bind(&ProjectTreeView::reqContinueToGenerate, q, item)); + else + act = menu->addAction(ProjectTreeView::tr("Generate"), this, std::bind(&ProjectTreeView::reqGenerateUTFile, q, item)); + act->setEnabled(!allIgnored); + + bool started = Utils::checkAnyState(item, Generating); + if (started) + menu->addAction(ProjectTreeView::tr("Stop"), this, std::bind(&ProjectTreeView::reqStopGenerate, q, item)); + + if (allIgnored) + menu->addAction(ProjectTreeView::tr("Unignore"), this, std::bind(&ProjectTreeViewPrivate::setItemIgnoreState, this, item, false)); + else + menu->addAction(ProjectTreeView::tr("Ignore"), this, std::bind(&ProjectTreeViewPrivate::setItemIgnoreState, this, item, true)); + + act = menu->addAction(ProjectTreeView::tr("Show Containing Folder"), this, [item] { + DDesktopServices::showFileItem(item->itemNode->filePath()); + }); + act->setEnabled(QFile::exists(item->itemNode->filePath())); +} + ProjectTreeView::ProjectTreeView(ViewType type, QWidget *parent) : DTreeView(parent), d(new ProjectTreeViewPrivate(this)) @@ -128,32 +163,8 @@ void ProjectTreeView::contextMenuEvent(QContextMenuEvent *event) return; QMenu menu; - if (d->viewType == UnitTest) { - if (item->itemNode->isFileNodeType()) { - auto act = menu.addAction(tr("Open File"), this, [item] { - editor.openFile(QString(), item->itemNode->filePath()); - }); - act->setEnabled(QFile::exists(item->itemNode->filePath())); - } - - bool allIgnored = Utils::checkAllState(item, Ignored); - auto act = menu.addAction(tr("Generate UT"), this, std::bind(&ProjectTreeView::reqGenerateUTFile, this, item)); - act->setEnabled(!allIgnored); - - bool started = Utils::checkAnyState(item, Generating); - if (started) - menu.addAction(tr("Stop"), this, std::bind(&ProjectTreeView::reqStopGenerate, this, item)); - - if (allIgnored) - menu.addAction(tr("Unignore"), this, std::bind(&ProjectTreeViewPrivate::setItemIgnoreState, d, item, false)); - else - menu.addAction(tr("Ignore"), this, std::bind(&ProjectTreeViewPrivate::setItemIgnoreState, d, item, true)); - - act = menu.addAction(tr("Show Containing Folder"), this, [item] { - DDesktopServices::showFileItem(item->itemNode->filePath()); - }); - act->setEnabled(QFile::exists(item->itemNode->filePath())); - } + if (d->viewType == UnitTest) + d->createUTActoins(&menu, item); if (!item->itemNode->isFileNodeType()) { menu.addSeparator(); @@ -165,5 +176,6 @@ void ProjectTreeView::contextMenuEvent(QContextMenuEvent *event) menu.addAction(tr("Expand All"), this, &ProjectTreeView::expandAll); } - menu.exec(QCursor::pos()); + if (!menu.actions().isEmpty()) + menu.exec(QCursor::pos()); } diff --git a/src/plugins/smartut/gui/projecttreeview.h b/src/plugins/smartut/gui/projecttreeview.h index 05e9979f2..1417ae30a 100644 --- a/src/plugins/smartut/gui/projecttreeview.h +++ b/src/plugins/smartut/gui/projecttreeview.h @@ -32,6 +32,7 @@ public Q_SLOTS: Q_SIGNALS: void reqGenerateUTFile(NodeItem *item); + void reqContinueToGenerate(NodeItem *item); void reqStopGenerate(NodeItem *item); protected: diff --git a/src/plugins/smartut/gui/smartutwidget.cpp b/src/plugins/smartut/gui/smartutwidget.cpp index 81b46b31f..5b9e693e9 100644 --- a/src/plugins/smartut/gui/smartutwidget.cpp +++ b/src/plugins/smartut/gui/smartutwidget.cpp @@ -9,21 +9,26 @@ #include "utils/utils.h" #include "event/eventreceiver.h" +#include "services/window/windowservice.h" + #include #include #include #include #include +#include DWIDGET_USE_NAMESPACE DGUI_USE_NAMESPACE +using namespace dpfservice; SmartUTWidget::SmartUTWidget(QWidget *parent) : QWidget(parent) { initUI(); initConnection(); + winSrv = dpfGetService(WindowService); } void SmartUTWidget::showSettingDialog() @@ -53,9 +58,11 @@ void SmartUTWidget::initUI() void SmartUTWidget::initConnection() { - connect(generateBtn, &DToolButton::clicked, this, qOverload<>(&SmartUTWidget::createUTFiles)); + connect(generateBtn, &DToolButton::clicked, this, &SmartUTWidget::generateAllUTFiles); + connect(continueBtn, &DToolButton::clicked, this, &SmartUTWidget::continueToGenerateAll); connect(stopBtn, &DToolButton::clicked, SmartUTManager::instance(), qOverload<>(&SmartUTManager::stop)); - connect(prjView, &ProjectTreeView::reqGenerateUTFile, this, qOverload(&SmartUTWidget::createUTFiles)); + connect(prjView, &ProjectTreeView::reqGenerateUTFile, this, &SmartUTWidget::generateUTFiles); + connect(prjView, &ProjectTreeView::reqContinueToGenerate, this, &SmartUTWidget::continueToGenerate); connect(prjView, &ProjectTreeView::reqStopGenerate, SmartUTManager::instance(), qOverload(&SmartUTManager::stop)); connect(SmartUTManager::instance(), &SmartUTManager::itemStateChanged, this, &SmartUTWidget::updateItemState); connect(EventDistributeProxy::instance(), &EventDistributeProxy::sigLLMCountChanged, this, &SmartUTWidget::updateModelList); @@ -118,28 +125,22 @@ QWidget *SmartUTWidget::createMainWidget() modelCB = new DComboBox(this); modelCB->addItems(SmartUTManager::instance()->modelList()); generateBtn = createButton("uc_generate", tr("Generate unit test files")); - runBtn = createButton("uc_run", tr("Run")); - reportBtn = createButton("uc_report", tr("Generate coverage report")); + continueBtn = createButton("uc_run", tr("Continue to generate")); stopBtn = createButton("uc_stop", tr("Stop")); - stopBtn->setVisible(false); + stopBtn->setEnabled(false); QHBoxLayout *bottomLayout = new QHBoxLayout; bottomLayout->setContentsMargins(10, 10, 10, 10); bottomLayout->addWidget(new DLabel(tr("Select Model:"), this)); bottomLayout->addWidget(modelCB, 1); + bottomLayout->addWidget(continueBtn); bottomLayout->addWidget(generateBtn); bottomLayout->addWidget(stopBtn); - bottomLayout->addWidget(runBtn); - bottomLayout->addWidget(reportBtn); layout->addWidget(prjView, 1); layout->addWidget(new DHorizontalLine(this)); layout->addLayout(bottomLayout); - // TODO: run and report - runBtn->setVisible(false); - reportBtn->setVisible(false); - return widget; } @@ -182,14 +183,66 @@ void SmartUTWidget::fillProjectView(const QString &workspace, const QStringList prjView->setRootProjectNode(prjNode); } -void SmartUTWidget::createUTFiles() +bool SmartUTWidget::checkModelValid() +{ + auto model = modelCB->currentText(); + QString errMsg; + bool valid = false; + auto llm = SmartUTManager::instance()->findModel(model); + if (!llm) { + errMsg = SmartUTManager::instance()->lastError(); + } else { + valid = llm->checkValid(&errMsg); + delete llm; + } + + if (!valid) + winSrv->notify(2, "SmartUT", errMsg, {}); + + return valid; +} + +void SmartUTWidget::generateAllUTFiles() +{ + generateUTFiles(prjView->rootItem()); +} + +void SmartUTWidget::generateUTFiles(NodeItem *item) { - SmartUTManager::instance()->generateUTFiles(modelCB->currentText(), prjView->rootItem()); + if (!checkModelValid()) + return; + + auto checkItemValid = [](NodeItem *item) { + return !item->hasChildren() + && item->itemNode->isFileNodeType() + && item->state != Ignored + && item->state != Waiting + && item->state != Generating; + }; + + SmartUTManager::instance()->generateUTFiles(modelCB->currentText(), item, checkItemValid); +} + +void SmartUTWidget::continueToGenerateAll() +{ + continueToGenerate(prjView->rootItem()); } -void SmartUTWidget::createUTFiles(NodeItem *item) +void SmartUTWidget::continueToGenerate(NodeItem *item) { - SmartUTManager::instance()->generateUTFiles(modelCB->currentText(), item); + if (!checkModelValid()) + return; + + auto checkItemValid = [](NodeItem *item) { + return !item->hasChildren() + && item->itemNode->isFileNodeType() + && item->state != Ignored + && item->state != Waiting + && item->state != Generating + && item->state != Completed; + }; + + SmartUTManager::instance()->generateUTFiles(modelCB->currentText(), item, checkItemValid); } void SmartUTWidget::updateModelList() @@ -203,10 +256,11 @@ void SmartUTWidget::updateModelList() void SmartUTWidget::updateItemState(NodeItem *item) { auto updateBtn = [this](bool isGenerating) { - generateBtn->setVisible(!isGenerating); - stopBtn->setVisible(isGenerating); + generateBtn->setEnabled(!isGenerating); + continueBtn->setEnabled(!isGenerating); + stopBtn->setEnabled(isGenerating); }; - + prjView->updateItem(item); if (item->state == Generating || item->state == Waiting) { updateBtn(true); diff --git a/src/plugins/smartut/gui/smartutwidget.h b/src/plugins/smartut/gui/smartutwidget.h index 908d862a7..8acb7002b 100644 --- a/src/plugins/smartut/gui/smartutwidget.h +++ b/src/plugins/smartut/gui/smartutwidget.h @@ -10,6 +10,10 @@ #include +namespace dpfservice { +class WindowService; +} + class ProjectTreeView; class SettingDialog; class NodeItem; @@ -22,8 +26,10 @@ class SmartUTWidget : public QWidget void showSettingDialog(); public Q_SLOTS: - void createUTFiles(); - void createUTFiles(NodeItem *item); + void generateAllUTFiles(); + void generateUTFiles(NodeItem *item); + void continueToGenerateAll(); + void continueToGenerate(NodeItem *item); void updateModelList(); void updateItemState(NodeItem *item); @@ -34,16 +40,17 @@ public Q_SLOTS: QWidget *createMainWidget(); void fillProjectView(const QString &workspace, const QStringList &fileList); + bool checkModelValid(); private: QStackedWidget *mainWidget { nullptr }; DTK_WIDGET_NAMESPACE::DComboBox *modelCB { nullptr }; DTK_WIDGET_NAMESPACE::DToolButton *generateBtn { nullptr }; + DTK_WIDGET_NAMESPACE::DToolButton *continueBtn { nullptr }; DTK_WIDGET_NAMESPACE::DToolButton *stopBtn { nullptr }; - DTK_WIDGET_NAMESPACE::DToolButton *runBtn { nullptr }; - DTK_WIDGET_NAMESPACE::DToolButton *reportBtn { nullptr }; ProjectTreeView *prjView { nullptr }; SettingDialog *settingDlg { nullptr }; + dpfservice::WindowService *winSrv { nullptr }; }; #endif // SMARTUTWIDGET_H diff --git a/src/plugins/smartut/gui/widget/promptsettingwidget.cpp b/src/plugins/smartut/gui/widget/promptsettingwidget.cpp index 3f778de42..20e6aef65 100644 --- a/src/plugins/smartut/gui/widget/promptsettingwidget.cpp +++ b/src/plugins/smartut/gui/widget/promptsettingwidget.cpp @@ -84,7 +84,7 @@ void PromptSettingWidget::updateSettings() } const auto &activePrompt = setting->value(kActiveGroup, kActivePrompt).toString(); - promptEdit->setPlainText(prompts.value(activePrompt).toString()); + promptCB->setCurrentText(activePrompt); } void PromptSettingWidget::handleAddPrompt() diff --git a/src/plugins/smartut/gui/widget/promptsettingwidget.h b/src/plugins/smartut/gui/widget/promptsettingwidget.h index 17d5d2d3e..e606ccf86 100644 --- a/src/plugins/smartut/gui/widget/promptsettingwidget.h +++ b/src/plugins/smartut/gui/widget/promptsettingwidget.h @@ -13,6 +13,7 @@ class PromptSettingWidget : public DTK_WIDGET_NAMESPACE::DFrame { + Q_OBJECT public: explicit PromptSettingWidget(QWidget *parent = nullptr); diff --git a/src/plugins/smartut/gui/widget/resourcesettingwidget.cpp b/src/plugins/smartut/gui/widget/resourcesettingwidget.cpp index 4bfcf17ac..5b1b9803a 100644 --- a/src/plugins/smartut/gui/widget/resourcesettingwidget.cpp +++ b/src/plugins/smartut/gui/widget/resourcesettingwidget.cpp @@ -111,7 +111,7 @@ void ResourceSettingWidget::handleProjectChanged() QString target = targetLocationEdit->text(); if (target.isEmpty()) { - QString path = prjInfo.workspaceFolder() + QDir::separator() + "test"; + QString path = prjInfo.workspaceFolder() + QDir::separator() + "tests"; targetLocationEdit->setText(path); } else if (prjInfoCache.isVaild() && target.startsWith(prjInfoCache.workspaceFolder())) { const auto &subFolder = target.mid(prjInfoCache.workspaceFolder().size()); diff --git a/src/plugins/smartut/manager/smartutmanager.cpp b/src/plugins/smartut/manager/smartutmanager.cpp index e4413d383..075a571ff 100644 --- a/src/plugins/smartut/manager/smartutmanager.cpp +++ b/src/plugins/smartut/manager/smartutmanager.cpp @@ -118,13 +118,7 @@ AbstractLLM *SmartUTManager::findModel(const QString &model) return nullptr; } - auto llm = d->aiSrv->getLLM(*iter); - // if (!llm->checkValid(&d->errorMsg)) { - // delete llm; - // return nullptr; - // } - - return llm; + return d->aiSrv->getLLM(*iter); } QString SmartUTManager::userPrompt(const QString &framework) const @@ -152,38 +146,22 @@ QString SmartUTManager::userPrompt(const QString &framework) const return prompt; } -void SmartUTManager::generateUTFiles(const QString &model, NodeItem *item) +void SmartUTManager::generateUTFiles(const QString &model, + NodeItem *item, + std::function checkItemValid) { - auto checkValid = [](NodeItem *item) { - return !item->hasChildren() - && item->itemNode->isFileNodeType() - && item->state != Ignored - && item->state != Waiting - && item->state != Generating; - }; - - if (checkValid(item)) { + if (checkItemValid(item)) { item->state = Waiting; item->userCache.clear(); Q_EMIT itemStateChanged(item); d->taskPool.addGenerateTask({ model, item }); } else if (item->hasChildren()) { for (int i = 0; i < item->rowCount(); ++i) { - generateUTFiles(model, static_cast(item->child(i))); + generateUTFiles(model, static_cast(item->child(i)), checkItemValid); } } } -void SmartUTManager::runTest(const dpfservice::ProjectInfo &prjInfo) -{ - //TODO: -} - -void SmartUTManager::generateCoverageReport(const dpfservice::ProjectInfo &prjInfo) -{ - //TODO: -} - void SmartUTManager::stop() { d->taskPool.stop(); diff --git a/src/plugins/smartut/manager/smartutmanager.h b/src/plugins/smartut/manager/smartutmanager.h index d2a4d3294..990289382 100644 --- a/src/plugins/smartut/manager/smartutmanager.h +++ b/src/plugins/smartut/manager/smartutmanager.h @@ -36,9 +36,9 @@ class SmartUTManager : public QObject QString lastError() const; public Q_SLOTS: - void generateUTFiles(const QString &model, NodeItem *item); - void runTest(const dpfservice::ProjectInfo &prjInfo); - void generateCoverageReport(const dpfservice::ProjectInfo &prjInfo); + void generateUTFiles(const QString &model, + NodeItem *item, + std::function checkItemValid); void stop(); void stop(NodeItem *item); diff --git a/src/plugins/smartut/manager/uttaskpool.cpp b/src/plugins/smartut/manager/uttaskpool.cpp index 3bbd60a11..be33d9568 100644 --- a/src/plugins/smartut/manager/uttaskpool.cpp +++ b/src/plugins/smartut/manager/uttaskpool.cpp @@ -138,7 +138,7 @@ void UTTaskPool::handleModelStateChanged(AbstractLLM *llm) } } -void UTTaskPool::createModels(const QString &model) +bool UTTaskPool::createModels(const QString &model) { QList models; for (int i = 0; i < kMaxModelCount; ++i) { diff --git a/src/plugins/smartut/manager/uttaskpool.h b/src/plugins/smartut/manager/uttaskpool.h index c49609af4..75ec6e373 100644 --- a/src/plugins/smartut/manager/uttaskpool.h +++ b/src/plugins/smartut/manager/uttaskpool.h @@ -44,7 +44,7 @@ public Q_SLOTS: void stoped(NodeItem *item); private: - void createModels(const QString &model); + bool createModels(const QString &model); QQueue taskQueue; QMap> idleModels;