From ca088fbaa665447b832fbc860297583a8312024c Mon Sep 17 00:00:00 2001 From: shenmo Date: Fri, 30 Oct 2026 22:27:16 +0800 Subject: [PATCH] =?UTF-8?q?feat(panel/chat):=20=E6=98=BE=E9=9A=90=E5=88=87?= =?UTF-8?q?=E6=8D=A2=E3=80=81=E6=9A=82=E5=AD=98=E6=B8=85=E7=A9=BA=E9=9A=90?= =?UTF-8?q?=E8=97=8F=E3=80=81=E8=8F=9C=E5=8D=95=E9=81=BF=E8=AE=A9=E3=80=81?= =?UTF-8?q?=E5=BE=85=E5=8A=9E=E8=81=94=E5=8A=A8=E3=80=81DTK=20=E5=85=B3?= =?UTF-8?q?=E4=BA=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 传送门 desktop 入口,--show 再点一次回 daemon;右键菜单加隐藏 - 暂存区清空后整个面板隐藏 - 右键菜单弹到面板左侧,避免被自己遮挡 - 启动悬停 80ms - 添加待办后立即打开待办窗口;待办独立图标 - 分词胶囊按浅/深色取色,修复透明不可见 - 关于改用 DAboutDialog - 聊天消息下方常驻 复制/编辑/重试;助手消息可编辑、可从任意一条重试 --- CMakeLists.txt | 4 +- data/gxde-ai-see-portal.desktop | 12 ++++++ data/gxde-ai-see-todo.desktop | 2 +- data/icons-src/gxde-ai-see-todo.svg | 17 ++++++++ .../hicolor/128x128/apps/gxde-ai-see-todo.png | Bin 0 -> 3058 bytes .../hicolor/16x16/apps/gxde-ai-see-todo.png | Bin 0 -> 560 bytes .../hicolor/24x24/apps/gxde-ai-see-todo.png | Bin 0 -> 855 bytes .../hicolor/256x256/apps/gxde-ai-see-todo.png | Bin 0 -> 6626 bytes .../hicolor/32x32/apps/gxde-ai-see-todo.png | Bin 0 -> 955 bytes .../hicolor/48x48/apps/gxde-ai-see-todo.png | Bin 0 -> 1552 bytes .../hicolor/64x64/apps/gxde-ai-see-todo.png | Bin 0 -> 1615 bytes src/app.cpp | 31 ++++++++++++-- src/app.h | 2 + src/chat/chatview.cpp | 28 ++++-------- src/chat/chatview.h | 2 +- src/chat/chatwindow.cpp | 40 ++++++++++++------ src/chat/chatwindow.h | 1 + src/dialogs/segmentdialog.cpp | 30 +++++++++---- src/todo/todowindow.cpp | 4 ++ src/ui/aboutdialog.cpp | 25 +++++++++++ src/ui/aboutdialog.h | 11 +++++ src/ui/portalpanel.cpp | 24 +++++++---- src/ui/portalpanel.h | 2 + 23 files changed, 180 insertions(+), 55 deletions(-) create mode 100644 data/gxde-ai-see-portal.desktop create mode 100644 data/icons-src/gxde-ai-see-todo.svg create mode 100644 data/icons/hicolor/128x128/apps/gxde-ai-see-todo.png create mode 100644 data/icons/hicolor/16x16/apps/gxde-ai-see-todo.png create mode 100644 data/icons/hicolor/24x24/apps/gxde-ai-see-todo.png create mode 100644 data/icons/hicolor/256x256/apps/gxde-ai-see-todo.png create mode 100644 data/icons/hicolor/32x32/apps/gxde-ai-see-todo.png create mode 100644 data/icons/hicolor/48x48/apps/gxde-ai-see-todo.png create mode 100644 data/icons/hicolor/64x64/apps/gxde-ai-see-todo.png create mode 100644 src/ui/aboutdialog.cpp create mode 100644 src/ui/aboutdialog.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 16ac4bb..7e76d47 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,7 @@ set(GXDE_AI_SEE_SOURCES src/chat/markdown.cpp src/chat/chatview.cpp src/ui/portalpanel.cpp + src/ui/aboutdialog.cpp ) add_executable(gxde-ai-see ${GXDE_AI_SEE_SOURCES}) @@ -119,7 +120,8 @@ if (LRELEASE_EXECUTABLE AND TS_FILES) endif() install(TARGETS gxde-ai-see RUNTIME DESTINATION bin) -install(FILES data/gxde-ai-see.desktop data/gxde-ai-see-todo.desktop DESTINATION share/applications) +install(FILES data/gxde-ai-see.desktop data/gxde-ai-see-todo.desktop + data/gxde-ai-see-portal.desktop DESTINATION share/applications) install(DIRECTORY data/icons/hicolor/ DESTINATION share/icons/hicolor) include(GNUInstallDirs) diff --git a/data/gxde-ai-see-portal.desktop b/data/gxde-ai-see-portal.desktop new file mode 100644 index 0000000..b71cc14 --- /dev/null +++ b/data/gxde-ai-see-portal.desktop @@ -0,0 +1,12 @@ +[Desktop Entry] +Type=Application +Version=1.0 +Name=GXDE Portal +Name[zh_CN]=传送门 +Comment=Show or hide the GXDE AI See portal panel +Comment[zh_CN]=显示或隐藏小埃同学的传送门面板 +Exec=gxde-ai-see --show +Icon=gxde-ai-see +Terminal=false +Categories=Utility; +StartupNotify=false diff --git a/data/gxde-ai-see-todo.desktop b/data/gxde-ai-see-todo.desktop index 1aaed5d..848bd69 100644 --- a/data/gxde-ai-see-todo.desktop +++ b/data/gxde-ai-see-todo.desktop @@ -6,7 +6,7 @@ Name[zh_CN]=GXDE 待办 Comment=Floating todo list of GXDE AI See Comment[zh_CN]=小埃同学的待办 Exec=gxde-ai-see --todo -Icon=gxde-ai-see +Icon=gxde-ai-see-todo Terminal=false Categories=Utility; StartupNotify=false diff --git a/data/icons-src/gxde-ai-see-todo.svg b/data/icons-src/gxde-ai-see-todo.svg new file mode 100644 index 0000000..76b3109 --- /dev/null +++ b/data/icons-src/gxde-ai-see-todo.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/data/icons/hicolor/128x128/apps/gxde-ai-see-todo.png b/data/icons/hicolor/128x128/apps/gxde-ai-see-todo.png new file mode 100644 index 0000000000000000000000000000000000000000..0f152529b92ef7ef524b9009dbbb5050124dda1d GIT binary patch literal 3058 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 y-079fe06GdSYsGR literal 0 HcmV?d00001 diff --git a/data/icons/hicolor/24x24/apps/gxde-ai-see-todo.png b/data/icons/hicolor/24x24/apps/gxde-ai-see-todo.png new file mode 100644 index 0000000000000000000000000000000000000000..af1a8bc7be9b304843054e89a36ce65707d2ed93 GIT binary patch literal 855 zcmV-d1E~CoP)AFt?Np@H-^C{ zr6X3|aNKM7*E<-(+0THJ^7^N4z?ozX(A@++o(b6-cM8;-aDE|q)Im%VMo($C*%-A- z3glD!71J+i0tI_=j{{& zb3P1UXdrv=->6e$F4Fo-Qy3BcG;#Cr=hHWSA?(|tTQ7y@voZmF(k0_7gJm7=16QI5=H@t?cg ztN7?dnWZ(u*fS5Eo*FGNHCoE0$irdS^1#}{Xcw6nj#$5MdH0)*?jCqK4B8kFDIO2t z%2Gl(7Agap_b=8s^=c1FU}ZgJbz?h4ijjTV>w$Bivnnb!8o<*70T;fj5rx8AhhkpY zpQYv6&4e2(27vy)Ou$!pV6*$jSHl36{(uWJW&XI8aQMY1cH&3JV~&pJQ&i*-#g@sQ zGHn;B^v7N29}4(7Sx_xZ!`CFsx6QwjZe#g&%iCxzOhYGH-H4j0A$MJ$`+AMn#(Rk) ze~)bApQh#VwYtybR_f%s08Gp-UBof7>n-mJvhds7@JS6Iu6f4V@DTsgClk1-jXn#Y z0ItrxP*timiM0#PTH|Y)T@GWd{Z^Y~^2+?PtI+mWv5l8cFO^$C^I)KL-{WjKHRgVp hmKLvmIZ|tnKLCVOuvA%`@-P4Z002ovPDHLkV1mq{nuq`Z literal 0 HcmV?d00001 diff --git a/data/icons/hicolor/256x256/apps/gxde-ai-see-todo.png b/data/icons/hicolor/256x256/apps/gxde-ai-see-todo.png new file mode 100644 index 0000000000000000000000000000000000000000..db14033bb78d75a334a9bcd51a2c40650e6b0c2e GIT binary patch literal 6626 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 Msk{ojLe?zsUszJ88UO$Q literal 0 HcmV?d00001 diff --git a/data/icons/hicolor/32x32/apps/gxde-ai-see-todo.png b/data/icons/hicolor/32x32/apps/gxde-ai-see-todo.png new file mode 100644 index 0000000000000000000000000000000000000000..2dc71be7a3c43e1b31d500dc54f5572e1d23f4a2 GIT binary patch literal 955 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 { showPanel(); if (m_panel) { @@ -112,14 +112,19 @@ bool App::init(bool daemon, bool forceShow, bool showTodo, bool showChat) m_panel->stopPortalSuppression(); m_panel->setDragging(false); } - if (m_model->isEmpty() && m_panel && m_panel->isVisible()) { + if (m_model->isEmpty() && !m_panelPinned && m_panel && m_panel->isVisible()) { m_panel->dismiss(); } }); connect(m_model, &StagingModel::countChanged, this, [this]() { - if (!m_daemon && !m_model->isEmpty() && m_panel) { + if (!m_model->isEmpty() && m_panel) { m_panel->popup(); + return; + } + /* cleared: the panel exists for the staging list, hide it again */ + if (!m_dragging && !m_panelPinned && m_panel && m_panel->isVisible()) { + m_panel->dismiss(); } }); @@ -131,6 +136,7 @@ bool App::init(bool daemon, bool forceShow, bool showTodo, bool showChat) } if (!daemon || forceShow || !m_model->isEmpty()) { showPanel(); + m_panelPinned = forceShow; } return true; @@ -144,6 +150,11 @@ void App::ensurePanel() m_panel = new PortalPanel(m_model); connect(m_panel, &PortalPanel::openChatRequested, this, &App::showChatWindow); + connect(m_panel, &PortalPanel::hideRequested, this, [this]() { + m_panelPinned = false; + m_panel->dismiss(); + }); + connect(m_panel, &PortalPanel::todoAdded, this, &App::showTodoWindow); connect(m_panel, &PortalPanel::askWithContent, this, [this](const QString &text, const QStringList &files) { showChatWindow(); @@ -159,6 +170,18 @@ void App::showPanel() m_panel->popup(); } +void App::togglePanel() +{ + ensurePanel(); + if (m_panel->isVisible() && m_panelPinned) { + m_panel->dismiss(); + m_panelPinned = false; + return; + } + m_panelPinned = true; + m_panel->popup(); +} + void App::showChatWindow() { if (!m_chatWindow) { diff --git a/src/app.h b/src/app.h index faa3295..c7fc553 100644 --- a/src/app.h +++ b/src/app.h @@ -32,6 +32,7 @@ public: private: void ensurePanel(); void showPanel(); + void togglePanel(); void showTodoWindow(); void showChatWindow(); @@ -42,4 +43,5 @@ private: QLocalServer *m_server = nullptr; bool m_daemon = false; bool m_dragging = false; + bool m_panelPinned = false; }; diff --git a/src/chat/chatview.cpp b/src/chat/chatview.cpp index 3cef516..03d857d 100644 --- a/src/chat/chatview.cpp +++ b/src/chat/chatview.cpp @@ -182,15 +182,16 @@ public: connect(fork, &QToolButton::clicked, this, [this]() { Q_EMIT forkRequested(m_index); }); actions->addWidget(fork); - if (m_isLast) { - auto *retry = actionButton(tr("重试"), m_actions); - connect(retry, &QToolButton::clicked, this, - [this]() { Q_EMIT retryRequested(); }); - actions->addWidget(retry); - } + auto *edit = actionButton(tr("编辑"), m_actions); + connect(edit, &QToolButton::clicked, this, + [this]() { Q_EMIT editRequested(m_index); }); + actions->addWidget(edit); + auto *retry = actionButton(tr("重试"), m_actions); + connect(retry, &QToolButton::clicked, this, + [this]() { Q_EMIT retryRequested(m_index); }); + actions->addWidget(retry); } actions->addStretch(); - m_actions->hide(); column->addWidget(m_actions); if (m_isUser) { @@ -220,21 +221,10 @@ signals: void copyRequested(int index); void editRequested(int index); void forkRequested(int index); - void retryRequested(); + void retryRequested(int index); void linkActivated(const QUrl &url); protected: - void enterEvent(QEnterEvent *event) override - { - m_actions->show(); - QWidget::enterEvent(event); - } - - void leaveEvent(QEvent *event) override - { - m_actions->hide(); - QWidget::leaveEvent(event); - } private: ChatMessage m_message; diff --git a/src/chat/chatview.h b/src/chat/chatview.h index 967801f..616829f 100644 --- a/src/chat/chatview.h +++ b/src/chat/chatview.h @@ -36,7 +36,7 @@ signals: void copyRequested(int index); void editRequested(int index); void forkRequested(int index); - void retryRequested(); + void retryRequested(int index); void linkActivated(const QUrl &url); protected: diff --git a/src/chat/chatwindow.cpp b/src/chat/chatwindow.cpp index 9f1831d..59bff17 100644 --- a/src/chat/chatwindow.cpp +++ b/src/chat/chatwindow.cpp @@ -5,6 +5,8 @@ #include "config.h" #include "chatview.h" + +#include "../ui/aboutdialog.h" #include "markdown.h" #include "mcpclient.h" @@ -555,7 +557,9 @@ void ChatWindow::setupUi() }); connect(m_messages, &ChatMessageList::editRequested, this, &ChatWindow::editMessage); connect(m_messages, &ChatMessageList::forkRequested, this, &ChatWindow::forkFrom); - connect(m_messages, &ChatMessageList::retryRequested, this, &ChatWindow::retryLast); + connect(m_messages, &ChatMessageList::retryRequested, this, [this](int index) { + retryFrom(index); + }); auto *menu = new QMenu(menuButton); menu->addAction(tr("压缩上下文"), this, [this]() { beginCompression(false); }); @@ -925,9 +929,6 @@ void ChatWindow::editMessage(int index) if (index < 0 || index >= conversation.messages.size()) { return; } - if (conversation.messages.at(index).role != QStringLiteral("user")) { - return; - } QDialog dialog(this); dialog.setWindowTitle(tr("编辑消息")); @@ -959,6 +960,27 @@ void ChatWindow::editMessage(int index) requestCompletion(); } +void ChatWindow::retryFrom(int index) +{ + if (m_client->busy() || m_compressing || m_current < 0) { + return; + } + ChatConversation &conversation = m_conversations[m_current]; + if (index < 0 || index >= conversation.messages.size()) { + return; + } + if (conversation.messages.at(index).role != QStringLiteral("assistant")) { + return; + } + + /* drop everything from the assistant answer on, then regenerate it */ + conversation.messages.resize(index); + conversation.updatedAt = QDateTime::currentMSecsSinceEpoch(); + save(); + renderConversation(); + requestCompletion(); +} + void ChatWindow::retryLast() { if (m_client->busy() || m_compressing || m_current < 0) { @@ -1028,15 +1050,7 @@ void ChatWindow::handleAnchor(const QUrl &url) void ChatWindow::showAbout() { - QMessageBox::about( - this, tr("关于小埃同学"), - tr("

GXDE 小埃同学

" - "

版本 %1

" - "

GXDE 桌面的 AI 助手:传送门、内容工厂、待办与对话。

" - "

许可证:GPL-3.0-or-later
" - "仓库:" - "gitee.com/GXDE-OS/gxde-ai-see

") - .arg(QCoreApplication::applicationVersion())); + showAboutDialog(this, tr("GXDE 桌面的 AI 助手:传送门、内容工厂、待办与对话。")); } void ChatWindow::appendError(const QString &message) diff --git a/src/chat/chatwindow.h b/src/chat/chatwindow.h index 084421a..eda8d7d 100644 --- a/src/chat/chatwindow.h +++ b/src/chat/chatwindow.h @@ -68,6 +68,7 @@ private: void sendPayload(); void editMessage(int index); void retryLast(); + void retryFrom(int index); void forkFrom(int index); void beginCompression(bool thenSend); void handleAnchor(const QUrl &url); diff --git a/src/dialogs/segmentdialog.cpp b/src/dialogs/segmentdialog.cpp index f134387..0d68aa4 100644 --- a/src/dialogs/segmentdialog.cpp +++ b/src/dialogs/segmentdialog.cpp @@ -6,6 +6,7 @@ #include "flowlayout.h" #include +#include #include #include #include @@ -21,12 +22,17 @@ SegmentDialog::SegmentDialog(const QStringList &words, QWidget *parent) setAttribute(Qt::WA_DeleteOnClose); resize(560, 420); + const bool dark = palette().color(QPalette::Window).lightness() < 128; + const QColor capsuleBg = dark ? QColor(255, 255, 255, 20) : QColor(0, 0, 0, 18); + const QColor capsuleBorder = dark ? QColor(255, 255, 255, 30) : QColor(0, 0, 0, 28); + const QColor textColor = palette().color(QPalette::WindowText); + auto *layout = new QVBoxLayout(this); layout->setContentsMargins(16, 14, 16, 14); layout->setSpacing(10); auto *hint = new QLabel(tr("按住并滑过词语可以连续选择,选中后复制。"), this); - hint->setStyleSheet(QStringLiteral("color: rgba(255,255,255,0.55); font-size: 12px;")); + hint->setStyleSheet(QStringLiteral("font-size: 12px;")); layout->addWidget(hint); auto *scroll = new QScrollArea(this); @@ -44,12 +50,20 @@ SegmentDialog::SegmentDialog(const QStringList &words, QWidget *parent) capsule->setCursor(Qt::PointingHandCursor); capsule->setFocusPolicy(Qt::NoFocus); capsule->setStyleSheet(QStringLiteral( - "QPushButton { color: rgba(255,255,255,0.85); background: rgba(255,255,255,0.07);" - " border: 1px solid rgba(255,255,255,0.10); border-radius: 11px;" - " padding: 3px 10px; font-size: 13px; }" - "QPushButton:hover { background: rgba(255,255,255,0.14); }" - "QPushButton:checked { background: rgba(74,144,255,0.55);" - " border-color: rgba(160,205,255,0.9); color: white; }")); + "QPushButton { color: %1; background: %2; border: 1px solid %3;" + " border-radius: 11px; padding: 3px 10px; font-size: 13px; }" + "QPushButton:hover { background: %4; }" + "QPushButton:checked { background: rgba(74,144,255,0.85);" + " border-color: rgba(74,144,255,1.0); color: white; }") + .arg(textColor.name(), + QStringLiteral("rgba(%1,%2,%3,%4)") + .arg(capsuleBg.red()) + .arg(capsuleBg.green()) + .arg(capsuleBg.blue()) + .arg(capsuleBg.alpha()), + capsuleBorder.name(), + dark ? QStringLiteral("rgba(255,255,255,30)") + : QStringLiteral("rgba(0,0,0,28)"))); capsule->installEventFilter(this); connect(capsule, &QPushButton::released, this, [this]() { m_selecting = false; }); flow->addWidget(capsule); @@ -60,7 +74,7 @@ SegmentDialog::SegmentDialog(const QStringList &words, QWidget *parent) auto *all = new QPushButton(tr("全选"), this); auto *none = new QPushButton(tr("清空"), this); m_count = new QLabel(tr("已选 0 个"), this); - m_count->setStyleSheet(QStringLiteral("color: rgba(255,255,255,0.55); font-size: 12px;")); + m_count->setStyleSheet(QStringLiteral("font-size: 12px;")); auto *copy = new QPushButton(tr("复制选中"), this); copy->setStyleSheet(QStringLiteral( "QPushButton { color: white; background: rgba(74,144,255,0.85); border: none;" diff --git a/src/todo/todowindow.cpp b/src/todo/todowindow.cpp index a29b4fd..f7ad43a 100644 --- a/src/todo/todowindow.cpp +++ b/src/todo/todowindow.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -145,6 +146,9 @@ TodoWindow::TodoWindow(TodoModel *model, QWidget *parent) , m_model(model) { setWindowTitle(tr("待办")); + setWindowIcon(QIcon::fromTheme( + QStringLiteral("gxde-ai-see-todo"), + QIcon(QStringLiteral("/usr/share/icons/hicolor/256x256/apps/gxde-ai-see-todo.png")))); setWindowFlags(Qt::FramelessWindowHint | Qt::Tool); setAttribute(Qt::WA_TranslucentBackground); setAttribute(Qt::WA_ShowWithoutActivating); diff --git a/src/ui/aboutdialog.cpp b/src/ui/aboutdialog.cpp new file mode 100644 index 0000000..8d4c756 --- /dev/null +++ b/src/ui/aboutdialog.cpp @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2026 GXDE OS Team +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "aboutdialog.h" + +#include +#include +#include + +#include + +void showAboutDialog(QWidget *parent, const QString &description) +{ + auto *dialog = new Dtk::Widget::DAboutDialog(parent); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->setWindowTitle(QObject::tr("关于小埃同学")); + dialog->setProductIcon(QApplication::windowIcon()); + dialog->setProductName(QObject::tr("GXDE 小埃同学")); + dialog->setVersion(QCoreApplication::applicationVersion()); + dialog->setDescription(description); + dialog->setLicense(QStringLiteral("GPL-3.0-or-later")); + dialog->setWebsiteName(QStringLiteral("gitee.com/GXDE-OS/gxde-ai-see")); + dialog->setWebsiteLink(QStringLiteral("https://gitee.com/GXDE-OS/gxde-ai-see")); + dialog->show(); +} diff --git a/src/ui/aboutdialog.h b/src/ui/aboutdialog.h new file mode 100644 index 0000000..ed88b4d --- /dev/null +++ b/src/ui/aboutdialog.h @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2026 GXDE OS Team +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +class QWidget; + +/** DTK native about dialog shared by the panel and the chat window. */ +void showAboutDialog(QWidget *parent, const QString &description); diff --git a/src/ui/portalpanel.cpp b/src/ui/portalpanel.cpp index 34cc994..1bf6894 100644 --- a/src/ui/portalpanel.cpp +++ b/src/ui/portalpanel.cpp @@ -11,6 +11,7 @@ #include "factory/contentclassifier.h" #include "factory/contentfactory.h" +#include "aboutdialog.h" #include "dialogs/segmentdialog.h" #include "dialogs/speechdialog.h" #include "todo/todomodel.h" @@ -580,7 +581,7 @@ public: setMinimumHeight(190); m_portalTimer.setSingleShot(true); - m_portalTimer.setInterval(150); + m_portalTimer.setInterval(80); connect(&m_portalTimer, &QTimer::timeout, this, [this]() { if (m_active && !m_suppressed) { Q_EMIT portalRequested(); @@ -1203,6 +1204,7 @@ void PortalPanel::runFactoryAction(const QString &id, const DropContent &content : content.summary(); TodoModel::instance()->add(text); toast(tr("已添加到待办")); + Q_EMIT todoAdded(); return; } if (id == QLatin1String("save_image")) { @@ -1401,6 +1403,7 @@ void PortalPanel::showContextMenu(const QPoint &globalPos) menu.addSeparator(); QAction *clear = menu.addAction(tr("清空暂存区")); + QAction *hide = menu.addAction(tr("隐藏")); QAction *about = menu.addAction(tr("关于")); QAction *quit = menu.addAction(tr("退出")); @@ -1420,11 +1423,22 @@ void PortalPanel::showContextMenu(const QPoint &globalPos) menuPos.setX(available.right() - menuSize.width()); } } + menuPos.setX(globalPos.x() - menuSize.width() - 10); + if (scr) { + const QRect available = scr->availableGeometry(); + if (menuPos.x() < available.left()) { + menuPos.setX(available.left()); + } + } QAction *chosen = menu.exec(menuPos); if (!chosen) { return; } + if (chosen == hide) { + Q_EMIT hideRequested(); + return; + } if (chosen == fixedPosition) { m_fixedPosition = fixedPosition->isChecked(); saveWindowState(); @@ -1469,13 +1483,7 @@ void PortalPanel::showContextMenu(const QPoint &globalPos) } else if (chosen == clear) { m_model->clearAll(); } else if (chosen == about) { - QMessageBox::about( - this, tr("关于小埃同学"), - tr("

GXDE 小埃同学

版本 %1

" - "

传送门、内容工厂、待办与小埃同学对话。

" - "

GPL-3.0-or-later · " - "gxde-ai-see

") - .arg(QCoreApplication::applicationVersion())); + showAboutDialog(nullptr, tr("传送门、内容工厂、待办与小埃同学对话。")); } else if (chosen == quit) { QCoreApplication::quit(); } diff --git a/src/ui/portalpanel.h b/src/ui/portalpanel.h index dabcd53..37e7edd 100644 --- a/src/ui/portalpanel.h +++ b/src/ui/portalpanel.h @@ -41,6 +41,8 @@ public: signals: void openChatRequested(); void askWithContent(const QString &text, const QStringList &files); + void hideRequested(); + void todoAdded(); protected: void showEvent(QShowEvent *event) override;