diff --git a/examples/test_toplevel_tag/main.cpp b/examples/test_toplevel_tag/main.cpp index 4dd37fc3dc..9097441b96 100644 --- a/examples/test_toplevel_tag/main.cpp +++ b/examples/test_toplevel_tag/main.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -46,12 +47,23 @@ int main(int argc, char **argv) descEdit->setPlaceholderText("Enter description..."); auto *setTagBtn = new QPushButton("Set Tag", central); auto *setDescBtn = new QPushButton("Set Description", central); + auto *fullscreenBtn = new QPushButton("Fullscreen", central); + auto *restoreBtn = new QPushButton("Restore", central); + auto *tagLabel = new QLabel("Supported Tags:", central); + auto *tagCombo = new QComboBox(central); + tagCombo->addItem("test:invalid tag"); + tagCombo->addItem("org.deepin.treeland.im-candidate-panel"); + tagCombo->addItem("org.deepin.treeland.privileged-overlay"); auto *statusLabel = new QLabel(central); layout->addWidget(tagEdit); layout->addWidget(descEdit); + layout->addWidget(tagLabel); + layout->addWidget(tagCombo); layout->addWidget(setTagBtn); layout->addWidget(setDescBtn); + layout->addWidget(fullscreenBtn); + layout->addWidget(restoreBtn); layout->addWidget(statusLabel); window.setCentralWidget(central); @@ -108,6 +120,22 @@ int main(int argc, char **argv) } }); + QObject::connect(fullscreenBtn, &QPushButton::clicked, + &window, &QWidget::showFullScreen); + + QObject::connect(restoreBtn, &QPushButton::clicked, + &window, &QWidget::showNormal); + + QObject::connect(tagCombo, + &QComboBox::currentIndexChanged, + &window, + [tagCombo, tagEdit](int index) { + const QString text = tagCombo->itemText(index); + if (!text.isEmpty()) { + tagEdit->setText(text); + } + }); + statusLabel->setText("Ready"); }); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index edfcc420b9..8ee1e9a3ee 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,7 +18,7 @@ find_package(QT NAMES Qt6 COMPONENTS Core REQUIRED) find_package(Qt6 CONFIG REQUIRED ShaderTools Concurrent RemoteObjects Network) find_package(Qt6 COMPONENTS Quick QuickControls2 REQUIRED) if(Qt6_VERSION VERSION_GREATER_EQUAL 6.10) - find_package(Qt6 REQUIRED COMPONENTS QuickPrivate) + find_package(Qt6 REQUIRED COMPONENTS QuickPrivate QuickTemplates2Private) endif() pkg_search_module(PIXMAN REQUIRED IMPORTED_TARGET pixman-1) pkg_search_module(WAYLAND REQUIRED IMPORTED_TARGET wayland-server) @@ -389,6 +389,7 @@ target_link_libraries(libtreeland Qt6::Quick Qt6::QuickControls2 Qt6::QuickPrivate + Qt6::QuickTemplates2Private Qt6::DBus Qt6::Concurrent Qt6::RemoteObjects diff --git a/src/common/treelandlogging.cpp b/src/common/treelandlogging.cpp index 8b80495ecc..2b819fb69e 100644 --- a/src/common/treelandlogging.cpp +++ b/src/common/treelandlogging.cpp @@ -87,3 +87,5 @@ Q_LOGGING_CATEGORY(lcTlDdm, "treeland.ddm") // Popup focus management Q_LOGGING_CATEGORY(lcTlPopupFocus, "treeland.popup.focus") +// XDG shell +Q_LOGGING_CATEGORY(lcTlShellXdg, "treeland.shell.xdg", QtInfoMsg) diff --git a/src/common/treelandlogging.h b/src/common/treelandlogging.h index 0a2b65eee6..d113e65a3f 100644 --- a/src/common/treelandlogging.h +++ b/src/common/treelandlogging.h @@ -90,5 +90,7 @@ Q_DECLARE_LOGGING_CATEGORY(lcTlDdm) // Popup focus management Q_DECLARE_LOGGING_CATEGORY(lcTlPopupFocus) +// XDG shell +Q_DECLARE_LOGGING_CATEGORY(lcTlShellXdg) #endif // TREELAND_LOGGING_H diff --git a/src/core/imcandidatepanelmanager.cpp b/src/core/imcandidatepanelmanager.cpp index d94b0175e3..12e64e74da 100644 --- a/src/core/imcandidatepanelmanager.cpp +++ b/src/core/imcandidatepanelmanager.cpp @@ -2,8 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "imcandidatepanelmanager.h" +#include "common/treelandlogging.h" #include "output/output.h" +#include "workspace/workspace.h" #include "rootsurfacecontainer.h" #include "shellhandler.h" #include "surface/surfacewrapper.h" @@ -78,28 +80,33 @@ xcb_atom_t IMCandidatePanelManager::imCandidatePanelAtom() const { return m_imCandidatePanelAtom; } - -bool IMCandidatePanelManager::checkAndApplyIMCandidatePanel( - SurfaceWrapper *wrapper, - WAYLIB_SERVER_NAMESPACE::WXdgToplevelSurface *surface) +bool IMCandidatePanelManager::checkAndApplyIMCandidatePanel(SurfaceWrapper *wrapper) { - if (surface->tag() != IM_CANDIDATE_PANEL) - return false; - if (wrapper->isIMCandidatePanel()) + auto *shellSurface = wrapper->shellSurface(); + if (auto *xdgSurface + = qobject_cast(shellSurface)) { + if (xdgSurface->tag() != IM_CANDIDATE_PANEL) + return false; + } else if (auto *xwaylandSurface + = qobject_cast(shellSurface)) { + if (!isIMCandidatePanel(xwaylandSurface)) + return false; + } else { return false; - applyIMCandidatePanel(wrapper); - return true; -} + } -bool IMCandidatePanelManager::checkAndApplyIMCandidatePanel( - SurfaceWrapper *wrapper, - WAYLIB_SERVER_NAMESPACE::WXWaylandSurface *surface) -{ - if (!isIMCandidatePanel(surface)) - return false; if (wrapper->isIMCandidatePanel()) return false; applyIMCandidatePanel(wrapper); + + if (auto *xdgSurface = qobject_cast(shellSurface)) { + qCDebug(lcTlShellXdg) << "IM candidate panel applied" << wrapper + << "appId =" << wrapper->appId() + << "tag =" << xdgSurface->tag(); + } else { + qCDebug(lcTlShellXdg) << "IM candidate panel applied (XWayland)" << wrapper + << "appId =" << wrapper->appId(); + } return true; } @@ -244,7 +251,7 @@ void IMCandidatePanelManager::onXwaylandPropertyChanged( bool value = IMCandidatePanelManager::parseIMCandidatePanelProperty(result, atom); s->setProperty("imCandidatePanel", value); if (wrapper) { - self->checkAndApplyIMCandidatePanel(wrapper, s); + self->checkAndApplyIMCandidatePanel(wrapper); } }); } diff --git a/src/core/imcandidatepanelmanager.h b/src/core/imcandidatepanelmanager.h index 445ad6ff24..2d74468689 100644 --- a/src/core/imcandidatepanelmanager.h +++ b/src/core/imcandidatepanelmanager.h @@ -17,7 +17,6 @@ class Output; namespace WAYLIB_SERVER_NAMESPACE { class WInputMethodHelper; -class WXdgToplevelSurface; class WXWayland; class WXWaylandSurface; } // namespace WAYLIB_SERVER_NAMESPACE @@ -35,10 +34,7 @@ class IMCandidatePanelManager : public QObject bool isIMCandidatePanel(SurfaceWrapper *wrapper) const; bool isIMCandidatePanel(WAYLIB_SERVER_NAMESPACE::WXWaylandSurface *surface) const; - bool checkAndApplyIMCandidatePanel(SurfaceWrapper *wrapper, - WAYLIB_SERVER_NAMESPACE::WXdgToplevelSurface *surface); - bool checkAndApplyIMCandidatePanel(SurfaceWrapper *wrapper, - WAYLIB_SERVER_NAMESPACE::WXWaylandSurface *surface); + bool checkAndApplyIMCandidatePanel(SurfaceWrapper *wrapper); xcb_atom_t imCandidatePanelAtom() const; // Parse IM candidate panel property from async result map diff --git a/src/core/rootsurfacecontainer.h b/src/core/rootsurfacecontainer.h index 6e2b04f742..1df2a22c34 100644 --- a/src/core/rootsurfacecontainer.h +++ b/src/core/rootsurfacecontainer.h @@ -58,6 +58,8 @@ class RootSurfaceContainer : public SurfaceContainer PopupZOrder = 5, CaptureLayerZOrder = 6, LockScreenZOrder = 7, + GlobalOverlayZOrder = 100, + PrivilegedOverlayZOrder = 200, // Privileged overlay, above lock screen & lock screen popups }; void init(WServer *server); diff --git a/src/core/shellhandler.cpp b/src/core/shellhandler.cpp index c3a060d4af..5a90d94f59 100644 --- a/src/core/shellhandler.cpp +++ b/src/core/shellhandler.cpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include @@ -57,6 +58,7 @@ WAYLIB_SERVER_USE_NAMESPACE #define TREELAND_XDG_SHELL_VERSION 5 +const QLatin1String PRIVILEGED_OVERLAY_TAG("org.deepin.treeland.privileged-overlay"); ShellHandler::ShellHandler(RootSurfaceContainer *rootContainer, WServer *server) : m_rootSurfaceContainer(rootContainer) @@ -66,6 +68,7 @@ ShellHandler::ShellHandler(RootSurfaceContainer *rootContainer, WServer *server) , m_topContainer(new LayerSurfaceContainer(rootContainer)) , m_overlayContainer(new LayerSurfaceContainer(rootContainer)) , m_popupContainer(new SurfaceContainer(rootContainer)) + , m_privilegedOverlayContainer(new SurfaceContainer(rootContainer)) , m_windowConfigStore(new WindowConfigStore(this)) { m_treelandForeignToplevel = server->attach(); @@ -90,7 +93,8 @@ ShellHandler::ShellHandler(RootSurfaceContainer *rootContainer, WServer *server) m_overlayContainer->setObjectName(QStringLiteral("OverlayContainer")); m_popupContainer->setZ(RootSurfaceContainer::PopupZOrder); m_popupContainer->setObjectName(QStringLiteral("PopupContainer")); - + m_privilegedOverlayContainer->setZ(RootSurfaceContainer::PrivilegedOverlayZOrder); + m_privilegedOverlayContainer->setObjectName(QStringLiteral("PrivilegedOverlayContainer")); connect(m_rootSurfaceContainer->outputModel(), &QAbstractItemModel::rowsInserted, this, @@ -407,6 +411,11 @@ ForeignToplevelManagerInterfaceV1 *ShellHandler::foreignToplevel() const return m_treelandForeignToplevel; } +SurfaceContainer *ShellHandler::privilegedOverlayContainer() const +{ + return m_privilegedOverlayContainer; +} + void ShellHandler::createComponent(QmlEngine *engine, QQuickItem *parentItem) { m_windowMenu = engine->createWindowMenu(Helper::instance()); @@ -481,6 +490,10 @@ void ShellHandler::init(WServer *server, WSeat *seat) &WInputMethodHelper::inputPopupSurfaceV2Removed, this, &ShellHandler::onInputPopupSurfaceV2Removed); + + auto *overlay = QQuickOverlay::overlay(m_rootSurfaceContainer->window()); + overlay->setZ(RootSurfaceContainer::GlobalOverlayZOrder); + overlay->setParentItem(m_rootSurfaceContainer); } WXWayland *ShellHandler::createXWayland(WServer *server, @@ -600,6 +613,8 @@ void ShellHandler::ensureXdgWrapper(WXdgToplevelSurface *surface, const QString handleDdeShellSurfaceAdded(surface->surface(), wrapper); } auto updateSurfaceWithParentContainer = [this, wrapper, surface] { + if (wrapper->surfaceRole() == SurfaceWrapper::SurfaceRole::PrivilegedOverlay) + return; updateWrapperContainer(wrapper, surface->parentSurface()); }; @@ -617,14 +632,59 @@ void ShellHandler::ensureXdgWrapper(WXdgToplevelSurface *surface, const QString } Q_EMIT surfaceWrapperAdded(wrapper); - // IM candidate panel detection via xdg-toplevel-tag - if (m_imCandidatePanelManager) { - QPointer wrapperPtr(wrapper); - QObject::connect(surface, &WXdgToplevelSurface::tagChanged, this, [this, surface, wrapperPtr]() { - if (wrapperPtr) - m_imCandidatePanelManager->checkAndApplyIMCandidatePanel(wrapperPtr, surface); - }); + QPointer wrapperPtr(wrapper); + QObject::connect(surface, &WXdgToplevelSurface::tagChanged, this, [this, wrapperPtr]() { + if (wrapperPtr) { + if (checkAndApplyPrivilegedOverlay(wrapperPtr)) + return; + if (m_imCandidatePanelManager->checkAndApplyIMCandidatePanel(wrapperPtr)) + return; + } + }); +} + +bool ShellHandler::checkAndApplyPrivilegedOverlay(SurfaceWrapper *wrapper) +{ + auto *xdgSurface = qobject_cast(wrapper->shellSurface()); + if (!xdgSurface) return false; + if (xdgSurface->tag() != PRIVILEGED_OVERLAY_TAG) { + if (wrapper->surfaceRole() == SurfaceWrapper::SurfaceRole::PrivilegedOverlay) + revokePrivilegedOverlay(wrapper); + return false; } + applyPrivilegedOverlay(wrapper); + return true; +} + +void ShellHandler::applyPrivilegedOverlay(SurfaceWrapper *wrapper) +{ + qCInfo(lcTlShellXdg) << "Privileged overlay promoted (tag)" + << "wrapper =" << wrapper << "appId =" << wrapper->appId(); + // Move from the original container to the privileged overlay container + if (auto *oldContainer = wrapper->container()) + oldContainer->removeSurface(wrapper); + m_privilegedOverlayContainer->addSurface(wrapper); + wrapper->disableWindowAnimation(); + wrapper->setSurfaceRole(SurfaceWrapper::SurfaceRole::PrivilegedOverlay); + wrapper->setPositionAutomatic(false); + wrapper->setHasInitializeContainer(true); + wrapper->setSkipDockPreView(true); + wrapper->setSkipMutiTaskView(true); +} + +void ShellHandler::revokePrivilegedOverlay(SurfaceWrapper *wrapper) +{ + qCInfo(lcTlShellXdg) << "Privileged overlay revoked (tag removed)" + << "wrapper =" << wrapper << "appId =" << wrapper->appId(); + // Move from the privileged overlay container back to the workspace + if (auto *oldContainer = wrapper->container()) + oldContainer->removeSurface(wrapper); + wrapper->setSurfaceRole(SurfaceWrapper::SurfaceRole::Normal); + wrapper->setPositionAutomatic(true); + wrapper->disableWindowAnimation(false); + wrapper->setSkipDockPreView(false); + wrapper->setSkipMutiTaskView(false); + m_workspace->addSurface(wrapper); } void ShellHandler::onXdgToplevelSurfaceRemoved(WXdgToplevelSurface *surface) @@ -868,7 +928,7 @@ void ShellHandler::ensureXwaylandWrapper(WXWaylandSurface *surface, const QStrin // IM candidate panel detection via XWayland xprop if (m_imCandidatePanelManager - && m_imCandidatePanelManager->checkAndApplyIMCandidatePanel(wrapper, surface)) { + && m_imCandidatePanelManager->checkAndApplyIMCandidatePanel(wrapper)) { Q_EMIT surfaceWrapperAdded(wrapper); return; } diff --git a/src/core/shellhandler.h b/src/core/shellhandler.h index 36fff52908..0ee63cea11 100644 --- a/src/core/shellhandler.h +++ b/src/core/shellhandler.h @@ -72,6 +72,7 @@ class ShellHandler : public QObject WAYLIB_SERVER_NAMESPACE::WServer *server); [[nodiscard]] Workspace *workspace() const; [[nodiscard]] SurfaceContainer *popupContainer() const; + [[nodiscard]] SurfaceContainer *privilegedOverlayContainer() const; [[nodiscard]] RootSurfaceContainer *rootSurfaceContainer() const; [[nodiscard]] ForeignToplevelManagerInterfaceV1 *foreignToplevel() const; @@ -169,7 +170,9 @@ private Q_SLOTS: void onInitialPropertiesReady(WAYLIB_SERVER_NAMESPACE::WXWaylandSurface *surface, const QString &appId, const QMap &result); - + bool checkAndApplyPrivilegedOverlay(SurfaceWrapper *wrapper); + void applyPrivilegedOverlay(SurfaceWrapper *wrapper); + void revokePrivilegedOverlay(SurfaceWrapper *wrapper); WAYLIB_SERVER_NAMESPACE::WXdgShell *m_xdgShell = nullptr; WAYLIB_SERVER_NAMESPACE::WLayerShell *m_layerShell = nullptr; TreelandWallpaperShellInterfaceV1 *m_wallpaperShell = nullptr; @@ -187,6 +190,7 @@ private Q_SLOTS: LayerSurfaceContainer *m_topContainer = nullptr; LayerSurfaceContainer *m_overlayContainer = nullptr; SurfaceContainer *m_popupContainer = nullptr; + SurfaceContainer *m_privilegedOverlayContainer = nullptr; IMCandidatePanelManager *m_imCandidatePanelManager = nullptr; QObject *m_windowMenu = nullptr; // Prelaunch wrappers created before binding to a real shell surface diff --git a/src/plugins/lockscreen/qml/ControlAction.qml b/src/plugins/lockscreen/qml/ControlAction.qml index 67982ca52b..dc3657d208 100644 --- a/src/plugins/lockscreen/qml/ControlAction.qml +++ b/src/plugins/lockscreen/qml/ControlAction.qml @@ -73,33 +73,38 @@ RowLayout { Layout.alignment: Qt.AlignHCenter iconName: "login_power" - function closePopup() { - powerList.close() - } - ToolTip { enabled: true visible: powerItem.hovered text: qsTr("Power") } - Popup { + Item { id: powerList - width: rootItem.width - height: 140 parent: rootItem + visible: powerItem.expand + width: rootItem.width + height: rootItem.height x: 0 - y: rootItem.height / 5 * 2 - modal: true - contentItem: PowerList { } - background: MouseArea { - onClicked: powerItem.closePopup() + y: 0 + + // Click outside the PowerList to close + MouseArea { + anchors.fill: parent + onClicked: { + powerItem.expand = false + } + } + + PowerList { + width: rootItem.width + height: 140 + x: 0 + y: rootItem.height / 5 * 2 } - onClosed: powerItem.expand = false } onClicked: { powerItem.expand = true - powerList.open() } } diff --git a/src/surface/surfacewrapper.cpp b/src/surface/surfacewrapper.cpp index f0e0615e2c..5dde1367ac 100644 --- a/src/surface/surfacewrapper.cpp +++ b/src/surface/surfacewrapper.cpp @@ -2206,7 +2206,8 @@ void SurfaceWrapper::setAlwaysOnTop(bool alwaysOnTop) bool SurfaceWrapper::showOnAllWorkspace() const { - if (m_type == Type::Layer || m_type == Type::XdgPopup || isInputPopupLike()) [[unlikely]] + if (m_type == Type::Layer || m_type == Type::XdgPopup || isInputPopupLike() + || surfaceRole() == SurfaceWrapper::SurfaceRole::PrivilegedOverlay) [[unlikely]] return true; return m_workspaceId == Workspace::ShowOnAllWorkspaceId; } diff --git a/src/surface/surfacewrapper.h b/src/surface/surfacewrapper.h index 1fc0ed380d..b443c58270 100644 --- a/src/surface/surfacewrapper.h +++ b/src/surface/surfacewrapper.h @@ -131,6 +131,7 @@ class SurfaceWrapper : public QQuickItem { Normal, Overlay, + PrivilegedOverlay, // Privileged overlay, exempt from lock-screen hiding }; Q_ENUM(SurfaceRole)