From a78b2909202022e4c2d096a4739b46ce132d67ef Mon Sep 17 00:00:00 2001 From: DingVero Date: Sat, 11 Apr 2026 10:40:15 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=B1=9E=E6=80=A7=E9=9D=A2?= =?UTF-8?q?=E6=9D=BF=E9=BB=98=E8=AE=A4=E4=B8=8D=E6=8A=98=E5=8F=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/gui/main_window/MainWindow.cpp | 92 ++++++++++++++++++++-- client/gui/props/EntityPropertySection.cpp | 6 +- 2 files changed, 89 insertions(+), 9 deletions(-) diff --git a/client/gui/main_window/MainWindow.cpp b/client/gui/main_window/MainWindow.cpp index 9355bf6..1f40e9d 100644 --- a/client/gui/main_window/MainWindow.cpp +++ b/client/gui/main_window/MainWindow.cpp @@ -26,11 +26,14 @@ #include #include #include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -40,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -298,6 +302,83 @@ namespace { constexpr const char* kMimeProjectNodeJson = "application/x-hfut-project-node+json"; +static bool guiRunningOnWayland() { + return QGuiApplication::platformName().contains(QLatin1String("wayland"), Qt::CaseInsensitive); +} + +// 全屏 + Tool 菜单(仅非 Wayland):先按锚点对齐,再按可用工作区夹紧。仅靠 move(锚点) 时菜单右缘常超出屏幕, +// 部分合成器会把窗口整体扔到屏幕最左侧。 +static void placeContextMenuOnScreen(QMenu* menu, const QPoint& anchorGlobal, QScreen* screenHint) { + if (!menu) { + return; + } + menu->adjustSize(); + const QSize sh = menu->size(); + QScreen* scr = screenHint; + if (!scr) { + scr = QGuiApplication::screenAt(anchorGlobal); + } + if (!scr) { + scr = QGuiApplication::primaryScreen(); + } + const QRect avail = scr ? scr->availableGeometry() : QRect(); + if (!avail.isValid()) { + menu->move(anchorGlobal); + return; + } + int x = anchorGlobal.x(); + int y = anchorGlobal.y(); + const int w = sh.width(); + const int h = sh.height(); + if (x + w > avail.right()) { + x = anchorGlobal.x() - w; + } + if (x + w > avail.right()) { + x = avail.right() - w + 1; + } + if (x < avail.left()) { + x = avail.left(); + } + if (y + h > avail.bottom()) { + y = anchorGlobal.y() - h; + } + if (y + h > avail.bottom()) { + y = avail.bottom() - h + 1; + } + if (y < avail.top()) { + y = avail.top(); + } + menu->move(x, y); +} + +// 主窗口全屏时(常见于 F11),以 QMainWindow 为父级的 Popup 菜单可能被压在全屏层之下不可见。 +// X11 等:用 Qt::Tool + 延迟 move 抬高叠放顺序;Wayland(KWin 等)下 Tool 顶层窗的客户端 setGeometry/move 常被忽略, +// 菜单会被摆在屏左,应交给 Qt 的 xdg_popup 与普通 exec(globalPos) 定位,故不在 Wayland 走 Tool 分支。 +QAction* execPopupMenuAboveFullscreen(QMenu& menu, const QPoint& globalPos, QWidget* topLevelRef) { + QWidget* w = topLevelRef ? topLevelRef->window() : nullptr; + if (w && (w->windowState() & Qt::WindowFullScreen) && !guiRunningOnWayland()) { + menu.setWindowFlag(Qt::Tool, true); + QScreen* menuScreen = w->screen(); + QPointer guard(&menu); + QObject::connect( + &menu, + &QMenu::aboutToShow, + &menu, + [guard, menuScreen]() { + if (!guard) { + return; + } + QTimer::singleShot(0, guard.data(), [guard, menuScreen]() { + if (guard) { + placeContextMenuOnScreen(guard.data(), QCursor::pos(), menuScreen); + } + }); + }, + Qt::SingleShotConnection); + } + return menu.exec(globalPos); +} + class ProjectTreeWidget final : public QTreeWidget { public: explicit ProjectTreeWidget(QWidget* parent = nullptr) : QTreeWidget(parent) { @@ -641,7 +722,7 @@ void MainWindow::createTimelineDock() { m_currentFrame = std::clamp(frame, 0, core::Project::kClipFixedFrames - 1); if (m_editorCanvas) m_editorCanvas->setCurrentFrame(m_currentFrame); - QAction* chosen = menu.exec(globalPos); + QAction* chosen = execPopupMenuAboveFullscreen(menu, globalPos, this); if (!chosen) { return; } @@ -2383,7 +2464,7 @@ void MainWindow::showProjectRootContextMenu(const QPoint& globalPos) { actPreview->setEnabled(canPreview); actBack->setEnabled(m_previewRequested); - QAction* chosen = menu.exec(globalPos); + QAction* chosen = execPopupMenuAboveFullscreen(menu, globalPos, this); if (!chosen) { return; } @@ -2498,7 +2579,8 @@ void MainWindow::rebuildCentralPages() { } QMenu menu(this); QAction* actRemove = menu.addAction(QStringLiteral("从列表中移除")); - QAction* chosen = menu.exec(m_welcomeRecentTree->viewport()->mapToGlobal(pos)); + QAction* chosen = + execPopupMenuAboveFullscreen(menu, m_welcomeRecentTree->viewport()->mapToGlobal(pos), this); if (chosen == actRemove) { const QString path = item->data(0, Qt::UserRole).toString(); if (!path.isEmpty()) { @@ -3509,7 +3591,7 @@ void MainWindow::showBackgroundContextMenu(const QPoint& globalPos) { QAction* actComputeDepth = menu.addAction(QStringLiteral("计算深度")); actComputeDepth->setEnabled(m_workspace.isOpen() && m_workspace.hasBackground()); - QAction* chosen = menu.exec(globalPos); + QAction* chosen = execPopupMenuAboveFullscreen(menu, globalPos, this); if (!chosen) { return; } @@ -3549,7 +3631,7 @@ void MainWindow::showBlackholeContextMenu(const QPoint& globalPos, const QString QMenu menu(this); QAction* actResolve = menu.addAction(QStringLiteral("修复")); - QAction* chosen = menu.exec(globalPos); + QAction* chosen = execPopupMenuAboveFullscreen(menu, globalPos, this); if (!chosen || chosen != actResolve) { return; } diff --git a/client/gui/props/EntityPropertySection.cpp b/client/gui/props/EntityPropertySection.cpp index b18d2c5..41f6536 100644 --- a/client/gui/props/EntityPropertySection.cpp +++ b/client/gui/props/EntityPropertySection.cpp @@ -123,8 +123,6 @@ EntityPropertySection::EntityPropertySection(QWidget* parent) lay->addWidget(m_introContent); lay->addStretch(1); - m_introContent->setVisible(false); - m_introSaveTimer = new QTimer(this); m_introSaveTimer->setSingleShot(true); connect(m_introSaveTimer, &QTimer::timeout, this, [this]() { @@ -136,6 +134,7 @@ EntityPropertySection::EntityPropertySection(QWidget* parent) connect(m_introToggle, &QToolButton::clicked, this, [this]() { setIntroSectionExpanded(!m_introContent->isVisible()); }); + setIntroSectionExpanded(true); connect(m_name, &QLineEdit::editingFinished, this, [this]() { if (m_name) { @@ -231,7 +230,7 @@ void EntityPropertySection::clearDisconnected() { m_introVideo->blockSignals(false); } if (m_introImages) m_introImages->clear(); - setIntroSectionExpanded(false); + setIntroSectionExpanded(true); m_introBulkUpdate = false; } @@ -308,7 +307,6 @@ void EntityPropertySection::applyState(const EntityPropertyUiState& s) { } } } - setIntroSectionExpanded(false); m_introBulkUpdate = false; }