feat(chat): DTK 主窗口 + 新模型;fix(panel): 菜单位置/工厂挤压与误隐藏
- ChatWindow 改为 DMainWindow + DTitlebar,图标按 DPR 取图 - DeepSeek 模型改为 deepseek-flash / deepseek-v4-pro,设置里可拉取 /models - 内容工厂右侧常驻「问问小埃同学」大按钮,移除传送门上的常驻入口 - 工厂出现时面板整体上移预留空间,不再挤压传送门 - 工厂隐藏改为检查指针是否仍在面板内,拖进工厂不再消失 - 右键菜单超出屏幕时向上弹出 - 移除 512 图标(由 293px 源图放大,发糊)
This commit is contained in:
@@ -19,6 +19,9 @@
|
||||
- MCP:stdio JSON-RPC,自动 initialize / tools-list,工具以 function calling 暴露给模型并在对话中执行
|
||||
- 合成器 portal(flake-wm / gxde-wlcom):org.gxde.Portal、缩略图坐标映射、
|
||||
原生 drop 到目标窗口
|
||||
- 对话窗口改用 DMainWindow/DTitlebar(DTK),消息气泡、会话列表重做
|
||||
- DeepSeek 模型更新为 deepseek-flash / deepseek-v4-pro,设置里可自动获取 /models
|
||||
- 内容工厂右侧常驻「问问小埃同学」大按钮;面板为工厂预留空间,不再上挤
|
||||
- 英文翻译(95 条)与翻译加载;gxde-k9 开机自启与保活开关;不支持环境只提示一次并保持待命
|
||||
|
||||
-- shenmo <shenmo@spark-app.store>
|
||||
|
||||
+1
-1
@@ -117,6 +117,6 @@ 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(DIRECTORY data/icons/ DESTINATION share/icons/hicolor)
|
||||
install(DIRECTORY data/icons/hicolor/ DESTINATION share/icons/hicolor)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 236 KiB |
Vendored
+1
-1
@@ -1,3 +1,3 @@
|
||||
shlibs:Depends=libc6 (>= 2.34), libdtk2widget6 (>= 6:6.3.8), libdtk6core (>= 6.0.48), libgcc-s1 (>= 3.0), libicu76 (>= 76.1-1~), liblayershellqtinterface6 (>= 6.1.0), libqt6core6t64 (>= 6.8.2), libqt6dbus6 (>= 6.1.2), libqt6gui6 (>= 6.1.2), libqt6network6 (>= 6.1.2), libqt6widgets6 (>= 6.3.0), libstdc++6 (>= 5)
|
||||
shlibs:Depends=libc6 (>= 2.34), libdtk2widget6 (>= 6:6.3.8), libdtk6core (>= 6.0.48), libgcc-s1 (>= 3.0), libicu76 (>= 76.1-1~), liblayershellqtinterface6 (>= 6.1.0), libqt6core6t64 (>= 6.8.2), libqt6dbus6 (>= 6.1.2), libqt6gui6 (>= 6.8.2), libqt6network6 (>= 6.1.2), libqt6widgets6 (>= 6.3.0), libstdc++6 (>= 5)
|
||||
misc:Depends=
|
||||
misc:Pre-Depends=
|
||||
|
||||
@@ -48,7 +48,7 @@ bool prepareRequest(const QVector<ChatMessage> &messages, bool stream,
|
||||
QJsonObject payload;
|
||||
payload.insert(QStringLiteral("model"),
|
||||
Config::instance()
|
||||
->get(QStringLiteral("chat/model"), QStringLiteral("deepseek-chat"))
|
||||
->get(QStringLiteral("chat/model"), QStringLiteral("deepseek-flash"))
|
||||
.toString());
|
||||
QJsonArray allMessages = jsonMessages;
|
||||
for (const QJsonValue &extra : extraMessages) {
|
||||
|
||||
+88
-9
@@ -39,6 +39,7 @@
|
||||
#include <QMimeData>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QPushButton>
|
||||
#include <DTitlebar>
|
||||
#include <QRegularExpression>
|
||||
#include <QSaveFile>
|
||||
#include <QScreen>
|
||||
@@ -46,6 +47,10 @@
|
||||
#include <QShortcut>
|
||||
#include <QTextCursor>
|
||||
#include <QTextBrowser>
|
||||
#include <QIcon>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QUrl>
|
||||
#include <QUuid>
|
||||
#include <QVBoxLayout>
|
||||
@@ -224,10 +229,13 @@ private:
|
||||
};
|
||||
|
||||
ChatWindow::ChatWindow(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
: Dtk::Widget::DMainWindow(parent)
|
||||
, m_client(new ChatClient(this))
|
||||
{
|
||||
setWindowTitle(tr("小埃同学"));
|
||||
titlebar()->setTitle(tr("小埃同学"));
|
||||
titlebar()->setIcon(QApplication::windowIcon());
|
||||
titlebar()->setAutoHideOnFullscreen(true);
|
||||
setMinimumSize(760, 520);
|
||||
resize(1000, 700);
|
||||
m_storePath = Config::instance()->dataDir() + QStringLiteral("/chat.json");
|
||||
@@ -235,6 +243,18 @@ ChatWindow::ChatWindow(QWidget *parent)
|
||||
setupUi();
|
||||
McpManager::instance()->reload();
|
||||
load();
|
||||
|
||||
/* DeepSeek renamed the API models; map the old presets once. */
|
||||
const QString storedModel =
|
||||
Config::instance()->get(QStringLiteral("chat/model")).toString();
|
||||
if (storedModel == QStringLiteral("deepseek-chat")) {
|
||||
Config::instance()->set(QStringLiteral("chat/model"), QStringLiteral("deepseek-flash"));
|
||||
Config::instance()->sync();
|
||||
} else if (storedModel == QStringLiteral("deepseek-reasoner")) {
|
||||
Config::instance()->set(QStringLiteral("chat/model"), QStringLiteral("deepseek-v4-pro"));
|
||||
Config::instance()->sync();
|
||||
}
|
||||
|
||||
m_current = m_conversations.isEmpty() ? -1 : qBound(0, m_current, m_conversations.size() - 1);
|
||||
updateSidebar();
|
||||
renderConversation();
|
||||
@@ -337,7 +357,9 @@ ChatWindow::ChatWindow(QWidget *parent)
|
||||
|
||||
void ChatWindow::setupUi()
|
||||
{
|
||||
auto *root = new QHBoxLayout(this);
|
||||
auto *central = new QWidget(this);
|
||||
setCentralWidget(central);
|
||||
auto *root = new QHBoxLayout(central);
|
||||
root->setContentsMargins(0, 0, 0, 0);
|
||||
root->setSpacing(0);
|
||||
|
||||
@@ -356,8 +378,11 @@ void ChatWindow::setupUi()
|
||||
brandRow->setSpacing(10);
|
||||
auto *logo = new QLabel(sidebarPane);
|
||||
logo->setFixedSize(34, 34);
|
||||
logo->setPixmap(QApplication::windowIcon().pixmap(34, 34));
|
||||
logo->setScaledContents(true);
|
||||
const qreal ratio = logo->devicePixelRatioF();
|
||||
QPixmap logoPixmap = QApplication::windowIcon().pixmap(
|
||||
QSize(qRound(34 * ratio), qRound(34 * ratio)));
|
||||
logoPixmap.setDevicePixelRatio(ratio);
|
||||
logo->setPixmap(logoPixmap);
|
||||
logo->setStyleSheet(QStringLiteral("border-radius: 8px;"));
|
||||
brandRow->addWidget(logo);
|
||||
|
||||
@@ -448,7 +473,7 @@ void ChatWindow::setupUi()
|
||||
m_titleLabel->setStyleSheet(QStringLiteral("color: rgba(255,255,255,0.92);"));
|
||||
headerLayout->addWidget(m_titleLabel);
|
||||
|
||||
m_modelLabel = new QLabel(QStringLiteral("deepseek-chat"), header);
|
||||
m_modelLabel = new QLabel(QStringLiteral("deepseek-flash"), header);
|
||||
m_modelLabel->setStyleSheet(QStringLiteral(
|
||||
"QLabel { color: rgba(160,200,240,0.75); background: rgba(74,144,255,0.13);"
|
||||
" border-radius: 8px; padding: 2px 8px; font-size: 10px; }"));
|
||||
@@ -694,7 +719,7 @@ void ChatWindow::updateHeader()
|
||||
m_titleLabel->setText(valid ? m_conversations.at(m_current).title : tr("小埃同学"));
|
||||
m_modelLabel->setText(Config::instance()
|
||||
->get(QStringLiteral("chat/model"),
|
||||
QStringLiteral("deepseek-chat"))
|
||||
QStringLiteral("deepseek-flash"))
|
||||
.toString());
|
||||
}
|
||||
|
||||
@@ -1046,13 +1071,67 @@ void ChatWindow::openSettings()
|
||||
|
||||
auto *modelBox = new QComboBox(&dialog);
|
||||
modelBox->setEditable(true);
|
||||
modelBox->addItems({ QStringLiteral("deepseek-chat"),
|
||||
modelBox->addItems({ QStringLiteral("deepseek-flash"),
|
||||
QStringLiteral("deepseek-reasoner") });
|
||||
modelBox->setCurrentText(Config::instance()
|
||||
->get(QStringLiteral("chat/model"),
|
||||
QStringLiteral("deepseek-chat"))
|
||||
QStringLiteral("deepseek-flash"))
|
||||
.toString());
|
||||
form->addRow(tr("模型"), modelBox);
|
||||
auto *modelRow = new QHBoxLayout;
|
||||
modelRow->setSpacing(6);
|
||||
modelRow->addWidget(modelBox, 1);
|
||||
auto *fetchModels = new QPushButton(tr("获取模型列表"), &dialog);
|
||||
modelRow->addWidget(fetchModels);
|
||||
form->addRow(tr("模型"), modelRow);
|
||||
|
||||
connect(fetchModels, &QPushButton::clicked, &dialog, [=, &dialog]() {
|
||||
QString base = baseEdit->text().trimmed();
|
||||
while (base.endsWith(QLatin1Char('/'))) {
|
||||
base.chop(1);
|
||||
}
|
||||
if (base.endsWith(QStringLiteral("/chat/completions"))) {
|
||||
base.chop(int(qstrlen("/chat/completions")));
|
||||
}
|
||||
QNetworkRequest request{QUrl(base + QStringLiteral("/models"))};
|
||||
request.setRawHeader("Authorization",
|
||||
"Bearer " + keyEdit->text().trimmed().toUtf8());
|
||||
auto *manager = new QNetworkAccessManager(&dialog);
|
||||
QNetworkReply *reply = manager->get(request);
|
||||
fetchModels->setEnabled(false);
|
||||
connect(reply, &QNetworkReply::finished, &dialog, [=]() {
|
||||
fetchModels->setEnabled(true);
|
||||
const QByteArray body = reply->readAll();
|
||||
const QJsonDocument document = QJsonDocument::fromJson(body);
|
||||
const QJsonArray models =
|
||||
document.object().value(QStringLiteral("data")).toArray();
|
||||
if (reply->error() != QNetworkReply::NoError || models.isEmpty()) {
|
||||
fetchModels->setText(tr("获取失败"));
|
||||
fetchModels->setToolTip(QString::fromUtf8(body).left(200));
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
const QString current = modelBox->currentText();
|
||||
modelBox->clear();
|
||||
for (const QJsonValue &value : models) {
|
||||
const QJsonObject object = value.toObject();
|
||||
const QString id = object.value(QStringLiteral("id")).toString();
|
||||
if (id.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
modelBox->addItem(id);
|
||||
const QString name = object.value(QStringLiteral("name")).toString();
|
||||
if (!name.isEmpty()) {
|
||||
modelBox->setItemData(modelBox->count() - 1, name, Qt::ToolTipRole);
|
||||
}
|
||||
}
|
||||
const int index = modelBox->findText(current);
|
||||
if (index >= 0) {
|
||||
modelBox->setCurrentIndex(index);
|
||||
}
|
||||
fetchModels->setText(tr("已更新"));
|
||||
reply->deleteLater();
|
||||
});
|
||||
});
|
||||
|
||||
auto *contextSpin = new QSpinBox(&dialog);
|
||||
contextSpin->setRange(4, 200);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QVector>
|
||||
#include <DMainWindow>
|
||||
#include <QWidget>
|
||||
|
||||
#include "chatclient.h"
|
||||
@@ -32,7 +33,7 @@ struct ChatConversation
|
||||
* conversation, an item targets that conversation).
|
||||
* Right: conversation view (drop targets the current conversation) and input.
|
||||
*/
|
||||
class ChatWindow : public QWidget
|
||||
class ChatWindow : public Dtk::Widget::DMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <QMimeData>
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
#include <QPushButton>
|
||||
#include <QStyle>
|
||||
#include <QToolButton>
|
||||
#include <QVBoxLayout>
|
||||
@@ -130,9 +131,13 @@ ContentFactory::ContentFactory(QWidget *parent)
|
||||
setAcceptDrops(true);
|
||||
setMinimumHeight(44);
|
||||
|
||||
auto *outer = new QVBoxLayout(this);
|
||||
auto *outer = new QHBoxLayout(this);
|
||||
outer->setContentsMargins(12, 7, 12, 7);
|
||||
outer->setSpacing(6);
|
||||
outer->setSpacing(10);
|
||||
|
||||
auto *left = new QVBoxLayout;
|
||||
left->setContentsMargins(0, 0, 0, 0);
|
||||
left->setSpacing(6);
|
||||
|
||||
auto *header = new QHBoxLayout;
|
||||
header->setContentsMargins(0, 0, 0, 0);
|
||||
@@ -155,7 +160,7 @@ ContentFactory::ContentFactory(QWidget *parent)
|
||||
hint->setStyleSheet(QStringLiteral("color: rgba(255,255,255,0.32); font-size: 11px;"));
|
||||
header->addWidget(hint);
|
||||
|
||||
outer->addLayout(header);
|
||||
left->addLayout(header);
|
||||
|
||||
m_actions = new QWidget(this);
|
||||
m_grid = new QGridLayout(m_actions);
|
||||
@@ -163,7 +168,23 @@ ContentFactory::ContentFactory(QWidget *parent)
|
||||
m_grid->setHorizontalSpacing(6);
|
||||
m_grid->setVerticalSpacing(6);
|
||||
m_actions->hide();
|
||||
outer->addWidget(m_actions);
|
||||
left->addWidget(m_actions);
|
||||
|
||||
outer->addLayout(left, 1);
|
||||
|
||||
m_askButton = new QPushButton(tr("问问\n小埃同学"), this);
|
||||
m_askButton->setCursor(Qt::PointingHandCursor);
|
||||
m_askButton->setFocusPolicy(Qt::NoFocus);
|
||||
m_askButton->setFixedWidth(92);
|
||||
m_askButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
|
||||
m_askButton->setStyleSheet(QStringLiteral(
|
||||
"QPushButton { color: white; background: rgba(74,144,255,0.22);"
|
||||
" border: 1px solid rgba(120,180,255,0.45); border-radius: 10px;"
|
||||
" font-size: 12px; }"
|
||||
"QPushButton:hover { background: rgba(74,144,255,0.38);"
|
||||
" border-color: rgba(160,205,255,0.8); }"));
|
||||
connect(m_askButton, &QPushButton::clicked, this, &ContentFactory::askRequested);
|
||||
outer->addWidget(m_askButton);
|
||||
}
|
||||
|
||||
void ContentFactory::setContent(const DropContent &content)
|
||||
|
||||
@@ -33,6 +33,7 @@ signals:
|
||||
void actionRequested(const QString &id, const DropContent &content);
|
||||
void heightChanged();
|
||||
void dragActivity();
|
||||
void askRequested();
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
@@ -42,6 +43,7 @@ private:
|
||||
void rebuild();
|
||||
|
||||
QLabel *m_summary = nullptr;
|
||||
class QPushButton *m_askButton = nullptr;
|
||||
QWidget *m_actions = nullptr;
|
||||
QGridLayout *m_grid = nullptr;
|
||||
DropContent m_content;
|
||||
|
||||
+5
-1
@@ -20,7 +20,11 @@ int main(int argc, char *argv[])
|
||||
app.setApplicationName(QStringLiteral("gxde-ai-see"));
|
||||
app.setApplicationDisplayName(QStringLiteral("GXDE 小埃同学"));
|
||||
app.setApplicationVersion(QStringLiteral("0.1.0"));
|
||||
app.setWindowIcon(QIcon::fromTheme(QStringLiteral("gxde-ai-see")));
|
||||
QIcon appIcon = QIcon::fromTheme(QStringLiteral("gxde-ai-see"));
|
||||
if (appIcon.isNull()) {
|
||||
appIcon = QIcon(QStringLiteral("/usr/share/icons/hicolor/256x256/apps/gxde-ai-see.png"));
|
||||
}
|
||||
app.setWindowIcon(appIcon);
|
||||
app.setQuitOnLastWindowClosed(false);
|
||||
|
||||
const QString locale = QLocale::system().name();
|
||||
|
||||
+46
-22
@@ -584,16 +584,6 @@ public:
|
||||
}
|
||||
});
|
||||
|
||||
m_chatButton = new QPushButton(tr("问问小埃 →"), this);
|
||||
m_chatButton->setCursor(Qt::PointingHandCursor);
|
||||
m_chatButton->setFlat(true);
|
||||
m_chatButton->setFocusPolicy(Qt::NoFocus);
|
||||
m_chatButton->setStyleSheet(QStringLiteral(
|
||||
"QPushButton { color: #9ecbff; background: transparent; border: none;"
|
||||
" font-size: 13px; }"
|
||||
"QPushButton:hover { color: #cfe5ff; }"));
|
||||
connect(m_chatButton, &QPushButton::clicked, this, &PortalZone::chatRequested);
|
||||
|
||||
m_hintLabel = new QLabel(this);
|
||||
m_hintLabel->setAlignment(Qt::AlignCenter);
|
||||
m_hintLabel->setWordWrap(true);
|
||||
@@ -627,7 +617,6 @@ protected:
|
||||
void resizeEvent(QResizeEvent *event) override
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
m_chatButton->setGeometry(12, height() - 42, width() - 24, 30);
|
||||
m_hintLabel->setGeometry(12, height() - 84, width() - 24, 36);
|
||||
}
|
||||
|
||||
@@ -741,7 +730,6 @@ private:
|
||||
m_hintTimer.start();
|
||||
}
|
||||
|
||||
QPushButton *m_chatButton = nullptr;
|
||||
QLabel *m_hintLabel = nullptr;
|
||||
QTimer m_hintTimer;
|
||||
QTimer m_portalTimer;
|
||||
@@ -826,7 +814,8 @@ void PortalPanel::setupWindow()
|
||||
anchors |= LayerShellQt::Window::AnchorRight;
|
||||
anchors |= LayerShellQt::Window::AnchorBottom;
|
||||
layer->setAnchors(anchors);
|
||||
layer->setMargins(QMargins(0, 0, m_rightMargin, m_bottomMargin));
|
||||
layer->setMargins(
|
||||
QMargins(0, 0, m_rightMargin, m_bottomMargin + m_factoryOffset));
|
||||
layer->setLayer(LayerShellQt::Window::LayerOverlay);
|
||||
layer->setKeyboardInteractivity(
|
||||
LayerShellQt::Window::KeyboardInteractivityNone);
|
||||
@@ -851,7 +840,6 @@ void PortalPanel::setupUi()
|
||||
m_zone = new PortalZone(this);
|
||||
m_zone->setFixedWidth(kZoneWidth);
|
||||
outer->addWidget(m_zone);
|
||||
connect(m_zone, &PortalZone::chatRequested, this, &PortalPanel::openChatRequested);
|
||||
connect(m_zone, &PortalZone::portalRequested, this, [this]() {
|
||||
PortalClient *portal = PortalClient::instance();
|
||||
if (portal->beginPortal()) {
|
||||
@@ -936,12 +924,19 @@ void PortalPanel::setupUi()
|
||||
|
||||
m_factoryTimer = new QTimer(this);
|
||||
m_factoryTimer->setSingleShot(true);
|
||||
m_factoryTimer->setInterval(280);
|
||||
m_factoryTimer->setInterval(220);
|
||||
connect(m_factoryTimer, &QTimer::timeout, this, [this]() {
|
||||
if (m_factory) {
|
||||
m_factory->clearContent();
|
||||
m_factory->hide();
|
||||
if (!m_factory || !m_factory->isVisible()) {
|
||||
return;
|
||||
}
|
||||
/* Dragging from the portal zone into the factory must not hide it:
|
||||
* only hide once the pointer is really outside the panel. */
|
||||
if (rect().contains(mapFromGlobal(QCursor::pos()))) {
|
||||
m_factoryTimer->start();
|
||||
return;
|
||||
}
|
||||
m_factory->clearContent();
|
||||
m_factory->hide();
|
||||
refreshLayout();
|
||||
});
|
||||
|
||||
@@ -950,8 +945,13 @@ void PortalPanel::setupUi()
|
||||
connect(m_view, &StagingView::dragContent, this, &PortalPanel::noteDragContent);
|
||||
connect(m_view, &StagingView::dragFinished, this, &PortalPanel::noteDragFinished);
|
||||
connect(m_factory, &ContentFactory::actionRequested, this, &PortalPanel::runFactoryAction);
|
||||
connect(m_factory, &ContentFactory::askRequested, this, &PortalPanel::openChatRequested);
|
||||
connect(m_factory, &ContentFactory::heightChanged, this, [this]() { refreshLayout(); });
|
||||
connect(m_factory, &ContentFactory::dragActivity, this, [this]() { m_factoryTimer->stop(); });
|
||||
connect(m_factory, &ContentFactory::dragActivity, this, [this]() {
|
||||
if (m_factoryTimer) {
|
||||
m_factoryTimer->start();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void PortalPanel::refreshLayout()
|
||||
@@ -980,6 +980,13 @@ void PortalPanel::updateGeometryForContent()
|
||||
const int totalHeight =
|
||||
kMargin * 2 + headerHeight + 6 + selectionHeight + listHeight + factoryHeight;
|
||||
resize(kPanelWidth, totalHeight);
|
||||
|
||||
/* Growing downwards would push the portal/s. Staging area off screen: raise
|
||||
* the panel by the factory height so the two columns stay where they are. */
|
||||
if (m_factoryOffset != factoryHeight) {
|
||||
m_factoryOffset = factoryHeight;
|
||||
applyMargins();
|
||||
}
|
||||
}
|
||||
|
||||
void PortalPanel::noteDragContent(const QMimeData *mime)
|
||||
@@ -1194,7 +1201,8 @@ void PortalPanel::applyMargins()
|
||||
#ifdef GXDE_HAVE_LAYERSHELLQT
|
||||
if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"))) {
|
||||
if (auto *layer = LayerShellQt::Window::get(windowHandle())) {
|
||||
layer->setMargins(QMargins(0, 0, m_rightMargin, m_bottomMargin));
|
||||
layer->setMargins(
|
||||
QMargins(0, 0, m_rightMargin, m_bottomMargin + m_factoryOffset));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1202,7 +1210,7 @@ void PortalPanel::applyMargins()
|
||||
if (QScreen *scr = screen() ? screen() : QGuiApplication::primaryScreen()) {
|
||||
const QRect available = scr->availableGeometry();
|
||||
move(available.right() - width() - m_rightMargin,
|
||||
available.bottom() - height() - m_bottomMargin);
|
||||
available.bottom() - height() - m_bottomMargin - m_factoryOffset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1322,7 +1330,23 @@ void PortalPanel::showContextMenu(const QPoint &globalPos)
|
||||
QAction *about = menu.addAction(tr("关于"));
|
||||
QAction *quit = menu.addAction(tr("退出"));
|
||||
|
||||
QAction *chosen = menu.exec(globalPos);
|
||||
menu.ensurePolished();
|
||||
const QSize menuSize = menu.sizeHint();
|
||||
QPoint menuPos = globalPos;
|
||||
QScreen *scr = screen() ? screen() : QGuiApplication::primaryScreen();
|
||||
if (scr) {
|
||||
const QRect available = scr->availableGeometry();
|
||||
if (menuPos.y() + menuSize.height() > available.bottom()) {
|
||||
menuPos.setY(globalPos.y() - menuSize.height());
|
||||
}
|
||||
if (menuPos.y() < available.top()) {
|
||||
menuPos.setY(available.top());
|
||||
}
|
||||
if (menuPos.x() + menuSize.width() > available.right()) {
|
||||
menuPos.setX(available.right() - menuSize.width());
|
||||
}
|
||||
}
|
||||
QAction *chosen = menu.exec(menuPos);
|
||||
if (!chosen) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ private:
|
||||
int m_rightMargin = 16;
|
||||
int m_bottomMargin = 16;
|
||||
int m_manualListHeight = 0;
|
||||
int m_factoryOffset = 0;
|
||||
bool m_fixedPosition = true;
|
||||
bool m_fixedSize = false;
|
||||
int m_dragMode = 0; // 0 none, 1 move, 2 resize
|
||||
|
||||
Reference in New Issue
Block a user