diff --git a/panels/dock/multitaskview/package/multitaskview.qml b/panels/dock/multitaskview/package/multitaskview.qml index 2e2b524c2..b165b7e78 100644 --- a/panels/dock/multitaskview/package/multitaskview.qml +++ b/panels/dock/multitaskview/package/multitaskview.qml @@ -34,10 +34,58 @@ AppletDockItem { sourceSize: Qt.size(Dock.MAX_DOCK_TASKMANAGER_ICON_SIZE, Dock.MAX_DOCK_TASKMANAGER_ICON_SIZE) } + readonly property bool useCompositorTooltip: Qt.platform.pluginName === "wayland" + property bool compositorTooltipActive: false + function compositorTooltipApplet() { + if (!useCompositorTooltip) + return null + + const applets = DS.appletList("org.deepin.ds.dock.taskmanager") + for (let i = 0; i < applets.length; ++i) { + if (applets[i].parent === Applet.parent) + return applets[i] + } + return null + } + + function hideTooltip() { + if (toolTipShowTimer.running) + toolTipShowTimer.stop() + + if (compositorTooltipActive) { + compositorTooltipActive = false + var applet = compositorTooltipApplet() + if (applet) + applet.hideItemPreview() + } else { + toolTip.close() + } + } + Timer { id: toolTipShowTimer interval: 50 onTriggered: { + var applet = compositorTooltipApplet() + if (applet) { + var itemPos = toggleworkspace.mapToItem(null, 0, 0) + var xOffset, yOffset, interval = 10 + if (!useColumnLayout) { + xOffset = itemPos.x + toggleworkspace.width / 2 + yOffset = (Panel.position == Dock.Bottom ? -interval : interval + Panel.dockSize) + } else { + xOffset = (Panel.position == Dock.Right ? -interval : interval + Panel.dockSize) + yOffset = itemPos.y + toggleworkspace.height / 2 + } + + compositorTooltipActive = false + if (applet.requestTooltip(toolTip.text, Panel.rootObject, xOffset, yOffset, Panel.position)) { + compositorTooltipActive = true + return + } + // 合成器协议不可用时回退到原生 tooltip,避免 tooltip 直接消失 + } + var point = toggleworkspace.mapToItem(null, toggleworkspace.width / 2, toggleworkspace.height / 2) toolTip.DockPanelPositioner.bounding = Qt.rect(point.x, point.y, toolTip.width, toolTip.height) toolTip.open() @@ -51,7 +99,7 @@ AppletDockItem { onTapped: function (eventPoint, buttons) { if (buttons === Qt.LeftButton) { Applet.openWorkspace() - toolTip.close() + hideTooltip() } } } @@ -62,7 +110,7 @@ AppletDockItem { gesturePolicy: TapHandler.WithinBounds onTapped: function (eventPoint, buttons) { Applet.openWorkspace() - toolTip.close() + hideTooltip() } } HoverHandler { @@ -70,11 +118,7 @@ AppletDockItem { if (hovered) { toolTipShowTimer.start() } else { - if (toolTipShowTimer.running) { - toolTipShowTimer.stop() - } - - toolTip.close() + hideTooltip() } } } diff --git a/panels/dock/taskmanager/abstractwindowmonitor.cpp b/panels/dock/taskmanager/abstractwindowmonitor.cpp index c8b198e7f..df5771d54 100644 --- a/panels/dock/taskmanager/abstractwindowmonitor.cpp +++ b/panels/dock/taskmanager/abstractwindowmonitor.cpp @@ -69,6 +69,16 @@ void AbstractWindowMonitor::requestUpdateWindowIconGeometry(const QModelIndex &i { } +bool AbstractWindowMonitor::requestTooltip(const QString &text, QWindow *relativePositionItem, int32_t previewXoffset, int32_t previewYoffset, uint32_t direction) +{ + Q_UNUSED(text) + Q_UNUSED(relativePositionItem) + Q_UNUSED(previewXoffset) + Q_UNUSED(previewYoffset) + Q_UNUSED(direction) + return false; +} + void AbstractWindowMonitor::requestWindowsView(const QModelIndexList &indexes) const { qDebug() << indexes; diff --git a/panels/dock/taskmanager/abstractwindowmonitor.h b/panels/dock/taskmanager/abstractwindowmonitor.h index dbc20dec4..0e0db9df6 100644 --- a/panels/dock/taskmanager/abstractwindowmonitor.h +++ b/panels/dock/taskmanager/abstractwindowmonitor.h @@ -45,6 +45,10 @@ class AbstractWindowMonitor : public QAbstractListModel, public AbstractTaskMana virtual void requestPreview(QAbstractItemModel *sourceModel, QWindow *relativePositionItem, int32_t previewXoffset, int32_t previewYoffset, uint32_t direction) = 0; + // 返回是否已交由合成器绘制,false 时调用方应回退到自身的 tooltip。 + virtual bool + requestTooltip(const QString &text, QWindow *relativePositionItem, int32_t previewXoffset, int32_t previewYoffset, uint32_t direction); + void requestWindowsView(const QModelIndexList &indexes) const override; virtual void presentWindows(QList windowsId) = 0; diff --git a/panels/dock/taskmanager/package/AppItem.qml b/panels/dock/taskmanager/package/AppItem.qml index 3ec95614d..2975e85cf 100644 --- a/panels/dock/taskmanager/package/AppItem.qml +++ b/panels/dock/taskmanager/package/AppItem.qml @@ -436,6 +436,9 @@ Item { } } + readonly property bool useCompositorTooltip: Qt.platform.pluginName === "wayland" + property bool compositorTooltipActive: false + Timer { id: previewTimer interval: 500 @@ -444,16 +447,34 @@ Item { property int xOffset: 0 property int yOffset: 0 onTriggered: { - if (root.windows.length != 0 || Qt.platform.pluginName === "wayland") { + if (root.windows.length != 0) { // 使用基于 modelIndex 的预览API,确保精确匹配 taskmanager.Applet.requestPreview(root.modelIndex, Panel.rootObject, xOffset, yOffset, Panel.position); } } } + Timer { + id: tooltipTimer + interval: 50 + running: false + repeat: false + property int xOffset: 0 + property int yOffset: 0 + onTriggered: { + var text = root.itemId === "dde-trash" ? root.name + "-" + taskmanager.Applet.getTrashTipText() : root.name + // 合成器协议不可用时回退到原生 tooltip,避免 tooltip 直接消失 + if (taskmanager.Applet.requestTooltip(text, Panel.rootObject, xOffset, yOffset, Panel.position)) { + root.compositorTooltipActive = true + } else { + root.compositorTooltipActive = false + toolTipShowTimer.start() + } + } + } function onEntered() { - if (windows.length === 0) { + if (windows.length === 0 && !useCompositorTooltip) { toolTipShowTimer.start() return } @@ -467,6 +488,16 @@ Item { xOffset = (Panel.position == 1 ? -interval : interval + Panel.dockSize) yOffset = itemPos.y + (root.height / 2) } + + if (windows.length === 0) { + // 无窗口应用:交给合成器绘制 tooltip,以便与窗口预览保持连续的过渡动画 + tooltipTimer.xOffset = xOffset + tooltipTimer.yOffset = yOffset + tooltipTimer.start() + return + } + + compositorTooltipActive = false previewTimer.xOffset = xOffset previewTimer.yOffset = yOffset previewTimer.start() @@ -481,8 +512,17 @@ Item { previewTimer.stop() } + if (tooltipTimer.running) { + tooltipTimer.stop() + } + if (windows.length === 0) { - toolTip.close() + if (useCompositorTooltip) { + compositorTooltipActive = false + taskmanager.Applet.hideItemPreview() + } else { + toolTip.close() + } return } closeItemPreview() @@ -537,6 +577,14 @@ Item { root.Drag.imageSource = result.url; }) } + // 按下时立即取消待显示的 tooltip,避免启动应用后 tooltip 才弹出 + if (tooltipTimer.running) { + tooltipTimer.stop() + } + if (compositorTooltipActive) { + compositorTooltipActive = false + taskmanager.Applet.hideItemPreview() + } toolTip.close() closeItemPreview() } @@ -632,6 +680,13 @@ Item { if (windows.length > 0 && toolTip.toolTipVisible) { toolTip.close() } + // 无窗口应用在悬停期间新开窗口时,从合成器 tooltip 切换到窗口预览 + if (windows.length > 0 && compositorTooltipActive) { + compositorTooltipActive = false + previewTimer.xOffset = tooltipTimer.xOffset + previewTimer.yOffset = tooltipTimer.yOffset + previewTimer.start() + } } onIconGlobalPointChanged: { diff --git a/panels/dock/taskmanager/taskmanager.cpp b/panels/dock/taskmanager/taskmanager.cpp index 02ef7189e..64a284684 100644 --- a/panels/dock/taskmanager/taskmanager.cpp +++ b/panels/dock/taskmanager/taskmanager.cpp @@ -317,6 +317,16 @@ void TaskManager::requestPreview(const QModelIndex &index, QObject *relativePosi m_windowMonitor->requestPreview(m_hoverPreviewModel, qobject_cast(relativePositionItem), previewXoffset, previewYoffset, direction); } +bool TaskManager::requestTooltip(const QString &text, QObject *relativePositionItem, int32_t previewXoffset, int32_t previewYoffset, uint32_t direction) +{ + if (!m_windowMonitor) { + qCWarning(taskManagerLog) << "TaskManager::requestTooltip: m_windowMonitor is null"; + return false; + } + + return m_windowMonitor->requestTooltip(text, qobject_cast(relativePositionItem), previewXoffset, previewYoffset, direction); +} + void TaskManager::requestWindowsView(const QModelIndexList &indexes) const { dataModel()->requestWindowsView(indexes); diff --git a/panels/dock/taskmanager/taskmanager.h b/panels/dock/taskmanager/taskmanager.h index 2f1ab4444..bdd277071 100644 --- a/panels/dock/taskmanager/taskmanager.h +++ b/panels/dock/taskmanager/taskmanager.h @@ -93,6 +93,8 @@ class TaskManager : public DS_NAMESPACE::DContainment, public AbstractTaskManage Q_INVOKABLE void requestUpdateWindowIconGeometry(const QModelIndex &index, const QRect &geometry, QObject *delegate = nullptr) const override; Q_INVOKABLE void requestPreview(const QModelIndex &index, QObject *relativePositionItem, int32_t previewXoffset, int32_t previewYoffset, uint32_t direction); + Q_INVOKABLE bool + requestTooltip(const QString &text, QObject *relativePositionItem, int32_t previewXoffset, int32_t previewYoffset, uint32_t direction); Q_INVOKABLE void requestWindowsView(const QModelIndexList &indexes) const override; Q_INVOKABLE QString desktopIdToAppId(const QString& desktopId); diff --git a/panels/dock/taskmanager/treelandwindowmonitor.cpp b/panels/dock/taskmanager/treelandwindowmonitor.cpp index d0ccaabc1..2675a281f 100644 --- a/panels/dock/taskmanager/treelandwindowmonitor.cpp +++ b/panels/dock/taskmanager/treelandwindowmonitor.cpp @@ -9,6 +9,7 @@ #include "taskmanager.h" #include "treelandwindow.h" +#include #include #include #include @@ -61,6 +62,12 @@ void TreeLandDockPreviewContext::showWindowsPreview(QByteArray windowsId, int32_ show(windowsId, previewXoffset, previewYoffset, direction); } +void TreeLandDockPreviewContext::showTooltip(const QString &text, int32_t previewXoffset, int32_t previewYoffset, uint32_t direction) +{ + m_isDockMouseAreaEnter = true; + show_tooltip(text, previewXoffset, previewYoffset, direction); +} + void TreeLandDockPreviewContext::hideWindowsPreview() { m_isDockMouseAreaEnter = false; @@ -126,57 +133,82 @@ void TreeLandWindowMonitor::hideItemPreview() m_dockPreview->hideWindowsPreview(); } +void TreeLandWindowMonitor::ensureDockPreviewContext(QWindow *relativePositionItem) +{ + if (!m_dockPreview.isNull()) + return; + + auto window = relativePositionItem; + if (!window) return; + auto waylandWindow = dynamic_cast(window->handle()); + if (!waylandWindow) return; + + auto context = m_foreignToplevelManager->get_dock_preview_context(waylandWindow->wlSurface()); + m_dockPreview.reset(new TreeLandDockPreviewContext(context)); + connect(window, &QWindow::visibleChanged, m_dockPreview.get(), [this]() { + m_dockPreview.reset(); + }); + connect(m_dockPreview.get(), &TreeLandDockPreviewContext::closed, this, [this]() { + // 当预览关闭时,发出信号清空过滤状态 + emit previewShouldClear(); + }); +} + void TreeLandWindowMonitor::requestPreview(QAbstractItemModel *sourceModel, QWindow *relativePositionItem, int32_t previewXoffset, int32_t previewYoffset, uint32_t direction) { - if (!m_foreignToplevelManager->isActive()) { + if (!m_foreignToplevelManager || !m_foreignToplevelManager->isActive()) { return; } - if (m_dockPreview.isNull()) { - auto window = relativePositionItem; - if (!window) return; - auto waylandWindow = dynamic_cast(window->handle()); - if (!waylandWindow) return; - - auto context = m_foreignToplevelManager->get_dock_preview_context(waylandWindow->wlSurface()); - m_dockPreview.reset(new TreeLandDockPreviewContext(context)); - connect(window, &QWindow::visibleChanged, m_dockPreview.get(), [this]() { - m_dockPreview.reset(); - }); - connect(m_dockPreview.get(), &TreeLandDockPreviewContext::closed, this, [this]() { - // 当预览关闭时,发出信号清空过滤状态 - emit previewShouldClear(); - }); + if (!sourceModel || sourceModel->rowCount() == 0) { + qWarning() << "requestPreview called without any window"; + return; } - m_dockPreview->m_isDockMouseAreaEnter = true; + ensureDockPreviewContext(relativePositionItem); + if (m_dockPreview.isNull()) + return; - if (sourceModel && sourceModel->rowCount() > 0) { - QVarLengthArray array; - for (int i = 0; i < sourceModel->rowCount(); ++i) { - QModelIndex index = sourceModel->index(i, 0); - uint32_t winId = index.data(TaskManager::WinIdRole).toUInt(); - if (winId != 0) { - array.append(winId); - } + QVarLengthArray array; + for (int i = 0; i < sourceModel->rowCount(); ++i) { + QModelIndex index = sourceModel->index(i, 0); + uint32_t winId = index.data(TaskManager::WinIdRole).toUInt(); + if (winId != 0) { + array.append(winId); } + } - if (!array.isEmpty()) { - QByteArray byteArray; - int size = array.size() * sizeof(uint32_t); - byteArray.resize(size); - memcpy(byteArray.data(), array.constData(), size); - m_dockPreview->showWindowsPreview(byteArray, previewXoffset, previewYoffset, direction); - } else { - m_dockPreview->show_tooltip("???", previewXoffset, previewYoffset, direction); - } - } else { - m_dockPreview->show_tooltip("???", previewXoffset, previewYoffset, direction); + if (array.isEmpty()) { + qWarning() << "requestPreview called without any valid window id"; + return; } + + QByteArray byteArray; + int size = array.size() * sizeof(uint32_t); + byteArray.resize(size); + memcpy(byteArray.data(), array.constData(), size); + m_dockPreview->showWindowsPreview(byteArray, previewXoffset, previewYoffset, direction); +} + +bool TreeLandWindowMonitor::requestTooltip(const QString &text, + QWindow *relativePositionItem, + int32_t previewXoffset, + int32_t previewYoffset, + uint32_t direction) +{ + if (!m_foreignToplevelManager || !m_foreignToplevelManager->isActive()) + return false; + + ensureDockPreviewContext(relativePositionItem); + if (m_dockPreview.isNull()) + return false; + + m_dockPreview->showTooltip(text, previewXoffset, previewYoffset, direction); + return true; } void TreeLandWindowMonitor::requestUpdateWindowIconGeometry(const QModelIndex &index, const QRect &geometry, QObject *delegate) const diff --git a/panels/dock/taskmanager/treelandwindowmonitor.h b/panels/dock/taskmanager/treelandwindowmonitor.h index ec06b4845..4aa8c9fa5 100644 --- a/panels/dock/taskmanager/treelandwindowmonitor.h +++ b/panels/dock/taskmanager/treelandwindowmonitor.h @@ -40,6 +40,7 @@ class TreeLandDockPreviewContext : public QWaylandClientExtensionTemplate> m_windows; QScopedPointer m_foreignToplevelManager; QScopedPointer m_dockPreview;