From 1083e55a5cbc4baa0f51b251b9460efd300349a6 Mon Sep 17 00:00:00 2001 From: Wang Zichong Date: Tue, 26 May 2026 20:05:11 +0800 Subject: [PATCH] fix: xembed tray popup window position correction for stashed container MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 之前托盘位置总是相对于任务栏的 wl_surface 的,会导致向上收起区域的偏移 位置不正确。此提交增加了托盘位于哪个区域的区分,并根据对应的位置决定 所基于的 wl_surface 是哪个。 PMS: BUG-360349 Log: --- panels/dock/DockCompositor.qml | 16 ++++++-- panels/dock/dockhelper.h | 5 ++- panels/dock/dockpanel.cpp | 6 ++- panels/dock/dockpanel.h | 8 +++- panels/dock/package/main.qml | 10 ++++- panels/dock/pluginmanagerextension.cpp | 14 ++++++- panels/dock/pluginmanagerextension_p.h | 13 +++++-- .../ActionLegacyTrayPluginDelegate.qml | 2 + panels/dock/waylanddockhelper.cpp | 37 +++++++++++++++++-- panels/dock/waylanddockhelper.h | 12 ++++-- 10 files changed, 97 insertions(+), 26 deletions(-) diff --git a/panels/dock/DockCompositor.qml b/panels/dock/DockCompositor.qml index d9d75ff5f..730cdfe40 100644 --- a/panels/dock/DockCompositor.qml +++ b/panels/dock/DockCompositor.qml @@ -20,6 +20,10 @@ Item { property alias dockSize: pluginManager.dockSize property var moveXEmbedWindowHandler: null + function notifyXEmbedWindowMoveResult(wid, success) { + pluginManager.notifyXEmbedWindowMoveResult(wid, success) + } + property ListModel trayPluginSurfaces: ListModel {} property ListModel quickPluginSurfaces: ListModel {} property ListModel fixedPluginSurfaces: ListModel {} @@ -115,11 +119,15 @@ Item { dockCompositor.popupClosed() } - onMoveXEmbedWindowRequested: (wid, pluginId, itemKey, dx, dy) => { - console.log("move xembed window requested:", wid, pluginId, itemKey, dx, dy) + onMoveXEmbedWindowRequested: (wid, pluginId, itemKey, dx, dy, anchorWindow) => { + console.log("move xembed window requested:", wid, pluginId, itemKey, dx, dy, "anchorWindow:", anchorWindow) if (typeof dockCompositor.moveXEmbedWindowHandler === 'function') { - var success = dockCompositor.moveXEmbedWindowHandler(wid, dx, dy) - pluginManager.notifyXEmbedWindowMoveResult(wid, success) + var sent = dockCompositor.moveXEmbedWindowHandler(wid, dx, dy, anchorWindow) + if (!sent) { + // Pre-check failed (e.g. manager not ready) — no wl_callback registered + pluginManager.notifyXEmbedWindowMoveResult(wid, false) + } + // If sent, result arrives asynchronously via xembedWindowMoveResult signal } else { console.warn("moveXEmbedWindowHandler not available") pluginManager.notifyXEmbedWindowMoveResult(wid, false) diff --git a/panels/dock/dockhelper.h b/panels/dock/dockhelper.h index 38f889b6e..0f2f50d12 100644 --- a/panels/dock/dockhelper.h +++ b/panels/dock/dockhelper.h @@ -8,6 +8,7 @@ #include "dockpanel.h" #include +#include namespace dock { class DockWakeUpArea; @@ -22,8 +23,8 @@ class DockHelper : public QObject void enterScreen(QScreen *screen); void leaveScreen(); - // Move XEmbed window relative to dock surface (no-op on X11) - virtual bool moveXEmbedWindow(uint32_t wid, double dx, double dy) { return false; } + // Move XEmbed window relative to anchor window's surface (no-op on X11) + virtual bool moveXEmbedWindow(uint32_t wid, double dx, double dy, QQuickWindow *anchorWindow = nullptr) { return false; } Q_SIGNALS: void isWindowOverlapChanged(bool overlap); diff --git a/panels/dock/dockpanel.cpp b/panels/dock/dockpanel.cpp index cf1b5fc51..1e8bb860e 100644 --- a/panels/dock/dockpanel.cpp +++ b/panels/dock/dockpanel.cpp @@ -163,6 +163,8 @@ bool DockPanel::init() if (QStringLiteral("wayland") == platformName) { // TODO: support get color type from wayland m_helper = new WaylandDockHelper(this); + connect(static_cast(m_helper), &WaylandDockHelper::xembedWindowMoveResult, + this, &DockPanel::xembedWindowMoveResult); } else if (QStringLiteral("xcb") == platformName) { QObject::connect(Dtk::Gui::DGuiApplicationHelper::instance(), &Dtk::Gui::DGuiApplicationHelper::themeTypeChanged, this, [this](){ @@ -490,10 +492,10 @@ void DockPanel::setIsResizing(bool resizing) emit isResizingChanged(m_isResizing); } -bool DockPanel::moveXEmbedWindow(uint32_t wid, double dx, double dy) +bool DockPanel::moveXEmbedWindow(uint32_t wid, double dx, double dy, QQuickWindow *anchorWindow) { if (m_helper) { - return m_helper->moveXEmbedWindow(wid, dx, dy); + return m_helper->moveXEmbedWindow(wid, dx, dy, anchorWindow); } return false; } diff --git a/panels/dock/dockpanel.h b/panels/dock/dockpanel.h index 6101fd2a5..8fcc14fd6 100644 --- a/panels/dock/dockpanel.h +++ b/panels/dock/dockpanel.h @@ -84,8 +84,9 @@ class DockPanel : public DS_NAMESPACE::DPanel, public QDBusContext Q_INVOKABLE void notifyDockPositionChanged(int offsetX, int offsetY); - // Move XEmbed window relative to dock surface (Wayland only) - Q_INVOKABLE bool moveXEmbedWindow(uint32_t wid, double dx, double dy); + // Move XEmbed window relative to anchor window's surface (Wayland only) + // anchorWindow: the window containing the plugin item (dock panel or popup window) + Q_INVOKABLE bool moveXEmbedWindow(uint32_t wid, double dx, double dy, QQuickWindow *anchorWindow = nullptr); bool showInPrimary() const; void setShowInPrimary(bool newShowInPrimary); @@ -138,6 +139,9 @@ private Q_SLOTS: void contextDraggingChanged(); void isResizingChanged(bool isResizing); + // Emitted when async XEmbed window move completes + void xembedWindowMoveResult(uint32_t wid, bool success); + private: ColorTheme m_theme; HideState m_hideState; diff --git a/panels/dock/package/main.qml b/panels/dock/package/main.qml index bde239b72..b7d7e87b1 100644 --- a/panels/dock/package/main.qml +++ b/panels/dock/package/main.qml @@ -855,10 +855,16 @@ Window { return Panel.devicePixelRatio }) - DockCompositor.moveXEmbedWindowHandler = function(wid, dx, dy) { - return Panel.moveXEmbedWindow(wid, dx, dy) + DockCompositor.moveXEmbedWindowHandler = function(wid, dx, dy, anchorWindow) { + return Panel.moveXEmbedWindow(wid, dx, dy, anchorWindow) + // If true: result arrives asynchronously via Panel.xembedWindowMoveResult signal + // If false: pre-check failed, caller should notify failure immediately } + Panel.xembedWindowMoveResult.connect(function(wid, success) { + DockCompositor.notifyXEmbedWindowMoveResult(wid, success) + }) + dock.itemIconSizeBase = dock.dockItemMaxSize dock.visible = Panel.hideState !== Dock.Hide changeDragAreaAnchor() diff --git a/panels/dock/pluginmanagerextension.cpp b/panels/dock/pluginmanagerextension.cpp index 9fb999e1d..c6ff5e517 100644 --- a/panels/dock/pluginmanagerextension.cpp +++ b/panels/dock/pluginmanagerextension.cpp @@ -264,6 +264,16 @@ QPoint PluginSurface::itemPosition() const return m_itemPosition; } +void PluginSurface::setAnchorWindow(QQuickWindow *window) +{ + m_anchorWindow = window; +} + +QQuickWindow *PluginSurface::anchorWindow() const +{ + return m_anchorWindow; +} + void PluginSurface::plugin_mouse_event(QtWaylandServer::plugin::Resource *resource, int32_t type) { Q_UNUSED(resource) @@ -706,8 +716,8 @@ void PluginManager::plugin_manager_v1_move_xembed_window(Resource *resource, uin // Store pending callback for later response (supports concurrent requests) m_pendingXEmbedCallbacks[xembed_winid] = {callback, resource->handle}; - // Emit signal with position info for QML to handle - Q_EMIT moveXEmbedWindowRequested(xembed_winid, plugin_id, item_key, dx, dy); + // Emit signal with position info and anchor window for QML to handle + Q_EMIT moveXEmbedWindowRequested(xembed_winid, plugin_id, item_key, dx, dy, pluginSurface->anchorWindow()); } void PluginManager::notifyXEmbedWindowMoveResult(uint32_t wid, bool success) diff --git a/panels/dock/pluginmanagerextension_p.h b/panels/dock/pluginmanagerextension_p.h index 1b7b7ea6e..45e94b101 100644 --- a/panels/dock/pluginmanagerextension_p.h +++ b/panels/dock/pluginmanagerextension_p.h @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -99,8 +100,8 @@ class PluginManager : public QWaylandCompositorExtensionTemplate, void dockSizeChanged(); void requestShutdown(const QString &type); // Signal emitted when XEmbed window move is requested - // Parameters: wid (window ID), pluginId, itemKey, dx (relative x offset), dy (relative y offset) - void moveXEmbedWindowRequested(uint32_t wid, const QString &pluginId, const QString &itemKey, double dx, double dy); + // Parameters: wid (window ID), pluginId, itemKey, dx, dy, anchorWindow (the window containing the plugin item) + void moveXEmbedWindowRequested(uint32_t wid, const QString &pluginId, const QString &itemKey, double dx, double dy, QQuickWindow *anchorWindow); private Q_SLOTS: void onFontChanged(); @@ -192,10 +193,13 @@ class PluginSurface : public QWaylandShellSurfaceTemplate, public Q_INVOKABLE void updatePluginGeometry(const QRect &geometry); Q_INVOKABLE void setGlobalPos(const QPoint &pos); + Q_INVOKABLE void setAnchorWindow(QQuickWindow *window); - // Position relative to the dock window, set from QML via updatePluginGeometry + // Position relative to the containing window, set from QML via updatePluginGeometry QPoint itemPosition() const; + QQuickWindow *anchorWindow() const; + int margins() const; void setMargins(int newMargins); @@ -234,7 +238,8 @@ class PluginSurface : public QWaylandShellSurfaceTemplate, public int m_margins = 0; int m_height; int m_width; - QPoint m_itemPosition; // Position relative to dock window, for XEmbed window positioning + QPoint m_itemPosition; + QPointer m_anchorWindow; }; class PluginPopup : public QWaylandShellSurfaceTemplate, public QtWaylandServer::plugin_popup diff --git a/panels/dock/tray/package/ActionLegacyTrayPluginDelegate.qml b/panels/dock/tray/package/ActionLegacyTrayPluginDelegate.qml index 2a7fe3919..b67f190db 100644 --- a/panels/dock/tray/package/ActionLegacyTrayPluginDelegate.qml +++ b/panels/dock/tray/package/ActionLegacyTrayPluginDelegate.qml @@ -92,6 +92,7 @@ AppletItemButton { if (!pluginItem.plugin || !itemVisible) return updatePluginMargins() + pluginItem.plugin.setAnchorWindow(pluginItem.Window.window) pluginItem.plugin.updatePluginGeometry(Qt.rect(pluginItem.itemGlobalPoint.x, pluginItem.itemGlobalPoint.y, 0, 0)) pluginItem.plugin.setGlobalPos(pluginItem.itemGlobalPos) } @@ -105,6 +106,7 @@ AppletItemButton { if (!pluginItem.plugin || !itemVisible) return updatePluginMargins() + pluginItem.plugin.setAnchorWindow(pluginItem.Window.window) if (pluginItem.itemGlobalPoint.x >= 0 && pluginItem.itemGlobalPoint.y >= 0) { pluginItem.plugin.updatePluginGeometry(Qt.rect(pluginItem.itemGlobalPoint.x, pluginItem.itemGlobalPoint.y, 0, 0)) } diff --git a/panels/dock/waylanddockhelper.cpp b/panels/dock/waylanddockhelper.cpp index 231806193..5663e0584 100644 --- a/panels/dock/waylanddockhelper.cpp +++ b/panels/dock/waylanddockhelper.cpp @@ -152,7 +152,7 @@ void WaylandDockHelper::setDockColorTheme(const ColorTheme &theme) m_panel->setColorTheme(theme); } -bool WaylandDockHelper::moveXEmbedWindow(uint32_t wid, double dx, double dy) +bool WaylandDockHelper::moveXEmbedWindow(uint32_t wid, double dx, double dy, QQuickWindow *anchorWindow) { // Update dock wl_surface if needed if (!m_dockWlSurface && m_panel->window()) { @@ -162,14 +162,43 @@ bool WaylandDockHelper::moveXEmbedWindow(uint32_t wid, double dx, double dy) } } - if (!m_ddeShellManager || !m_ddeShellManager->isActive() || !m_dockWlSurface) { + struct ::wl_surface *anchorSurface = m_dockWlSurface; + + if (anchorWindow) { + auto waylandWindow = dynamic_cast(anchorWindow->handle()); + if (waylandWindow && waylandWindow->waylandSurface()) { + anchorSurface = waylandWindow->waylandSurface()->object(); + } + } + + if (!m_ddeShellManager || !m_ddeShellManager->isActive() || !anchorSurface) { qWarning() << "WaylandDockHelper::moveXEmbedWindow: not ready, manager active:" << (m_ddeShellManager && m_ddeShellManager->isActive()) - << "surface:" << (m_dockWlSurface != nullptr); + << "surface:" << (anchorSurface != nullptr); return false; } - m_ddeShellManager->setXWindowPositionRelative(wid, m_dockWlSurface, dx, dy); + struct wl_callback *cb = m_ddeShellManager->setXWindowPositionRelative(wid, anchorSurface, dx, dy); + + // Register wl_callback listener — result arrives asynchronously + if (!cb) { + qWarning() << "WaylandDockHelper::moveXEmbedWindow: setXWindowPositionRelative returned null callback"; + Q_EMIT xembedWindowMoveResult(wid, false); + return true; + } + + struct CallbackData { uint32_t wid; WaylandDockHelper *helper; }; + static struct wl_callback_listener s_callbackListener = { + .done = [](void *data, wl_callback *callback, uint32_t callback_data) { + auto *d = static_cast(data); + bool success = (callback_data == 0); + Q_EMIT d->helper->xembedWindowMoveResult(d->wid, success); + wl_callback_destroy(callback); + delete d; + } + }; + wl_callback_add_listener(cb, &s_callbackListener, new CallbackData{wid, this}); + return true; } diff --git a/panels/dock/waylanddockhelper.h b/panels/dock/waylanddockhelper.h index d48dc9342..2fd93433b 100644 --- a/panels/dock/waylanddockhelper.h +++ b/panels/dock/waylanddockhelper.h @@ -31,10 +31,14 @@ class WaylandDockHelper : public DockHelper void setDockColorTheme(const ColorTheme &theme); QString dockScreenName(); - // Move XEmbed window relative to dock surface - // dx, dy: offset relative to dock surface top-left - // Returns true if the request was sent successfully - bool moveXEmbedWindow(uint32_t wid, double dx, double dy) override; + // Move XEmbed window relative to anchor window's surface + // anchorWindow: the window containing the plugin item; falls back to dock panel if null + // dx, dy: offset relative to anchor window top-left + // Returns true if the request was sent (does NOT guarantee completion) + bool moveXEmbedWindow(uint32_t wid, double dx, double dy, QQuickWindow *anchorWindow = nullptr) override; + +Q_SIGNALS: + void xembedWindowMoveResult(uint32_t wid, bool success); protected: bool currentActiveWindowFullscreened() override;