From 3c179e69327014ee529d18825c03004958211501 Mon Sep 17 00:00:00 2001 From: Flavio Date: Sun, 15 Nov 2026 11:16:09 -0300 Subject: [PATCH] feat(dock): mirror dock across all screens Add an opt-in dock instance for every connected screen. Keep mirrored wake areas and tray loading scoped to the target screen, synchronize the global appearance theme, and recover dock geometry when monitors are hotplugged or their layout is still settling. --- panels/dock/dconfig/org.deepin.ds.dock.json | 10 ++ panels/dock/dockhelper.cpp | 14 +- panels/dock/dockpanel.cpp | 109 ++++++++++++++- panels/dock/dockpanel.h | 6 + panels/dock/docksettings.cpp | 22 +++ panels/dock/docksettings.h | 5 + shell/main.cpp | 141 ++++++++++++++++++-- 7 files changed, 288 insertions(+), 19 deletions(-) diff --git a/panels/dock/dconfig/org.deepin.ds.dock.json b/panels/dock/dconfig/org.deepin.ds.dock.json index 8da9bc01d..730cf6d6f 100755 --- a/panels/dock/dconfig/org.deepin.ds.dock.json +++ b/panels/dock/dconfig/org.deepin.ds.dock.json @@ -72,6 +72,16 @@ "permissions": "readwrite", "visibility": "private" }, + "Show_On_All_Screens": { + "value": false, + "serial": 0, + "flags": [], + "name": "show_on_all_screens", + "name[zh_CN]": "任务栏显示在所有屏幕", + "description": "show a mirrored dock on every connected screen", + "permissions": "readwrite", + "visibility": "private" + }, "Locked": { "value": false, "serial": 0, diff --git a/panels/dock/dockhelper.cpp b/panels/dock/dockhelper.cpp index e3767bcb1..972f5d323 100644 --- a/panels/dock/dockhelper.cpp +++ b/panels/dock/dockhelper.cpp @@ -26,6 +26,7 @@ DockHelper::DockHelper(DockPanel *parent) QMetaObject::invokeMethod(this, &DockHelper::checkNeedShowOrNot, Qt::QueuedConnection); connect(parent, &DockPanel::rootObjectChanged, this, &DockHelper::initAreas); + connect(parent, &DockPanel::dockScreenChanged, this, &DockHelper::initAreas); connect(parent, &DockPanel::showInPrimaryChanged, this, &DockHelper::updateAllDockWakeArea); connect(parent, &DockPanel::hideStateChanged, this, &DockHelper::updateAllDockWakeArea); connect(parent, &DockPanel::hideModeChanged, m_hideTimer, static_cast(&QTimer::start)); @@ -141,6 +142,8 @@ bool DockHelper::eventFilter(QObject *watched, QEvent *event) bool DockHelper::wakeUpAreaNeedShowOnThisScreen(QScreen *screen) { auto isDockAllowedShownOnThisScreen = [this, screen]() -> bool { + if (parent()->mirroredInstance()) + return screen == parent()->dockScreen(); return (parent()->showInPrimary() && screen == qApp->primaryScreen()) || !parent()->showInPrimary(); }; @@ -301,11 +304,18 @@ void DockHelper::initAreas() return; // init areas - for (auto screen : qApp->screens()) { - m_areas.insert(screen, createArea(screen)); + if (parent()->mirroredInstance()) { + if (auto *screen = parent()->dockScreen()) + m_areas.insert(screen, createArea(screen)); + } else { + for (auto screen : qApp->screens()) { + m_areas.insert(screen, createArea(screen)); + } } connect(qApp, &QGuiApplication::screenAdded, this, [this](QScreen *screen) { + if (parent()->mirroredInstance() && screen != parent()->dockScreen()) + return; if (m_areas.contains(screen)) return; m_areas.insert(screen, createArea(screen)); diff --git a/panels/dock/dockpanel.cpp b/panels/dock/dockpanel.cpp index 7e3d54dbf..e120615ab 100644 --- a/panels/dock/dockpanel.cpp +++ b/panels/dock/dockpanel.cpp @@ -19,6 +19,7 @@ #include "dockdaemonadaptor.h" #include +#include #include #include #include @@ -26,6 +27,7 @@ #include #include #include +#include #include @@ -196,6 +198,47 @@ bool DockPanel::init() Q_EMIT dockDaemonAdaptor->LockedChanged(locked()); }); + auto appearanceConfig = DConfig::create(QStringLiteral("org.deepin.dde.appearance"), + QStringLiteral("org.deepin.dde.appearance"), + QString(), this); + if (appearanceConfig && appearanceConfig->isValid()) { + auto dtkThemeConfig = DConfig::create(QStringLiteral("org.deepin.dtk.preference"), + QStringLiteral("org.deepin.dtk.preference"), + QString(), this); + auto controlCenterThemeConfig = DConfig::create(QStringLiteral("org.deepin.dde.control-center"), + QStringLiteral("org.deepin.dtk.preference"), + QString(), this); + auto fileManagerThemeConfig = DConfig::create(QStringLiteral("dde-file-manager"), + QStringLiteral("org.deepin.dtk.preference"), + QString(), this); + auto shellThemeConfig = DConfig::create(QStringLiteral("org.deepin.dde.shell"), + QStringLiteral("org.deepin.dtk.preference"), + QString(), this); + const auto applyGlobalTheme = [this, appearanceConfig, dtkThemeConfig, + controlCenterThemeConfig, fileManagerThemeConfig, + shellThemeConfig]() { + const auto globalTheme = appearanceConfig->value(QStringLiteral("Global_Theme"), QString()).toString(); + const auto theme = globalTheme.contains(QStringLiteral("dark"), Qt::CaseInsensitive) ? Dark : Light; + const auto themeType = theme == Dark ? 2 : 1; + const auto applyThemeType = [themeType](DConfig *config) { + if (config && config->isValid()) + config->setValue(QStringLiteral("themeType"), themeType); + }; + applyThemeType(dtkThemeConfig); + applyThemeType(controlCenterThemeConfig); + applyThemeType(fileManagerThemeConfig); + applyThemeType(shellThemeConfig); + Dtk::Gui::DGuiApplicationHelper::instance()->setPaletteType(static_cast(theme)); + setColorTheme(theme); + }; + applyGlobalTheme(); + connect(appearanceConfig, &DConfig::valueChanged, this, + [applyGlobalTheme](const QString &key) { + if (key == QStringLiteral("Global_Theme")) + applyGlobalTheme(); + }); + } + DPanel::init(); QObject::connect(this, &DApplet::rootObjectChanged, this, [this]() { @@ -206,14 +249,24 @@ bool DockPanel::init() // connect(window(), &QQuickWindow::yChanged, this, &DockPanel::onWindowGeometryChanged); connect(window(), &QQuickWindow::widthChanged, this, &DockPanel::onWindowGeometryChanged); connect(window(), &QQuickWindow::heightChanged, this, &DockPanel::onWindowGeometryChanged); - QMetaObject::invokeMethod(this, &DockPanel::onWindowGeometryChanged); - if (showInPrimary()) + if (mirroredInstance()) { + const auto targetName = appletData().value(QStringLiteral("DockScreen")).toString(); + for (auto *screen : qApp->screens()) { + if (screen->name() == targetName) { + setDockScreen(screen); + break; + } + } + if (!m_dockScreen) + m_dockScreen = window()->screen(); + } else if (showInPrimary()) updateDockScreen(); else { m_dockScreen = window()->screen(); } + QMetaObject::invokeMethod(this, &DockPanel::onWindowGeometryChanged); // 监听屏幕几何信息变化,当主屏位置变化时重新计算 frontendWindowRect - if (auto screen = window()->screen()) { + if (auto screen = m_dockScreen) { connect(screen, &QScreen::geometryChanged, this, &DockPanel::onWindowGeometryChanged); } rootObject()->installEventFilter(this); @@ -221,7 +274,7 @@ bool DockPanel::init() } }); - m_theme = static_cast(Dtk::Gui::DGuiApplicationHelper::instance()->themeType()); + setColorTheme(static_cast(Dtk::Gui::DGuiApplicationHelper::instance()->themeType())); auto platformName = QGuiApplication::platformName(); if (QStringLiteral("wayland") == platformName) { m_helper = new WaylandDockHelper(this); @@ -480,6 +533,8 @@ void DockPanel::updateDockScreen() auto *win = window(); if (!win) return; + if (mirroredInstance()) + return; auto *primary = qApp->primaryScreen(); setDockScreen(primary); @@ -498,6 +553,21 @@ bool DockPanel::showInPrimary() const return SETTINGS->showInPrimary(); } +bool DockPanel::showOnAllScreens() const +{ + return SETTINGS->showOnAllScreens(); +} + +void DockPanel::setShowOnAllScreens(bool newShowOnAllScreens) +{ + SETTINGS->setShowOnAllScreens(newShowOnAllScreens); +} + +bool DockPanel::mirroredInstance() const +{ + return appletData().value(QStringLiteral("DockMirror")).toBool(); +} + void DockPanel::setShowInPrimary(bool newShowInPrimary) { if (SETTINGS->showInPrimary() == newShowInPrimary) @@ -550,6 +620,37 @@ void DockPanel::setDockScreen(QScreen *screen) return; m_dockScreen = screen; window()->setScreen(m_dockScreen); + if (mirroredInstance()) { + QMetaObject::invokeMethod(this, [this, screen] { + if (!screen || !window() || m_dockScreen != screen || !qApp->screens().contains(screen)) + return; + + window()->setScreen(screen); + const auto screenGeometry = screen->geometry(); + const auto windowWidth = window()->width(); + const auto windowHeight = window()->height(); + int x = screenGeometry.x() + (screenGeometry.width() - windowWidth) / 2; + int y = screenGeometry.y() + (screenGeometry.height() - windowHeight) / 2; + + switch (position()) { + case Top: + y = screenGeometry.y(); + break; + case Right: + x = screenGeometry.right() + 1 - windowWidth; + break; + case Bottom: + y = screenGeometry.bottom() + 1 - windowHeight; + break; + case Left: + x = screenGeometry.x(); + break; + } + + window()->setPosition(x, y); + onWindowGeometryChanged(); + }, Qt::QueuedConnection); + } Q_EMIT dockScreenChanged(m_dockScreen); Q_EMIT screenNameChanged(); } diff --git a/panels/dock/dockpanel.h b/panels/dock/dockpanel.h index c69789b3a..105d8ba1e 100644 --- a/panels/dock/dockpanel.h +++ b/panels/dock/dockpanel.h @@ -35,6 +35,8 @@ class DockPanel : public DS_NAMESPACE::DPanel, public QDBusContext Q_PROPERTY(bool fashionMode READ fashionMode NOTIFY fashionModeChanged FINAL) Q_PROPERTY(IndicatorStyle indicatorStyle READ indicatorStyle WRITE setIndicatorStyle NOTIFY indicatorStyleChanged FINAL) Q_PROPERTY(bool showInPrimary READ showInPrimary WRITE setShowInPrimary NOTIFY showInPrimaryChanged FINAL) + Q_PROPERTY(bool showOnAllScreens READ showOnAllScreens WRITE setShowOnAllScreens NOTIFY showOnAllScreensChanged FINAL) + Q_PROPERTY(bool mirroredInstance READ mirroredInstance CONSTANT FINAL) Q_PROPERTY(QString screenName READ screenName NOTIFY screenNameChanged FINAL) Q_PROPERTY(bool locked READ locked WRITE setLocked NOTIFY lockedChanged FINAL) Q_PROPERTY(bool isResizing READ isResizing WRITE setIsResizing NOTIFY isResizingChanged FINAL) @@ -108,6 +110,9 @@ class DockPanel : public DS_NAMESPACE::DPanel, public QDBusContext bool showInPrimary() const; void setShowInPrimary(bool newShowInPrimary); + bool showOnAllScreens() const; + void setShowOnAllScreens(bool newShowOnAllScreens); + bool mirroredInstance() const; bool locked() const; void setLocked(bool newLocked); @@ -157,6 +162,7 @@ private Q_SLOTS: void cardCurrentChanged(const QString &cardCurrent); void indicatorStyleChanged(IndicatorStyle style); void showInPrimaryChanged(bool showInPrimary); + void showOnAllScreensChanged(bool showOnAllScreens); void dockScreenChanged(QScreen *screen); void screenNameChanged(); void requestClosePopup(); diff --git a/panels/dock/docksettings.cpp b/panels/dock/docksettings.cpp index adfbe5678..ca7d435d7 100644 --- a/panels/dock/docksettings.cpp +++ b/panels/dock/docksettings.cpp @@ -27,6 +27,7 @@ const static QString keyIndicatorStyle = "Indicator_Style"; const static QString keyPluginsVisible = "Plugins_Visible"; const static QString keyCardCurrent = "cardCurrent"; const static QString keyShowInPrimary = "Show_In_Primary"; +const static QString keyShowOnAllScreens = "Show_On_All_Screens"; const static QString keyLocked = "Locked"; const static QString keyEnableContextMenu = "enableContextMenu"; const static QString keyEnableFashionMode = "enableFashionMode"; @@ -163,6 +164,7 @@ DockSettings::DockSettings(QObject* parent) , m_dockPosition(dock::Bottom) , m_alignment(dock::CenterAlignment) , m_style(dock::Fashion) + , m_showOnAllScreens(false) , m_locked(false) , m_contextMenuEnabled(true) { @@ -190,6 +192,7 @@ void DockSettings::init() m_pluginsVisible = m_dockConfig->value(keyPluginsVisible).toMap(); m_cardCurrent = m_dockConfig->value(keyCardCurrent).toString(); m_showInPrimary = m_dockConfig->value(keyShowInPrimary).toBool(); + m_showOnAllScreens = m_dockConfig->value(keyShowOnAllScreens, false).toBool(); m_locked = m_dockConfig->value(keyLocked).toBool(); m_contextMenuEnabled = m_dockConfig->value(keyEnableContextMenu, true).toBool(); m_fashionModeEnabled = m_dockConfig->value(keyEnableFashionMode, false).toBool(); @@ -238,6 +241,11 @@ void DockSettings::init() if (showInPrimary == m_showInPrimary) return; m_showInPrimary = showInPrimary; Q_EMIT showInPrimaryChanged(m_showInPrimary); + } else if (keyShowOnAllScreens == key) { + const auto showOnAllScreens = m_dockConfig->value(keyShowOnAllScreens).toBool(); + if (showOnAllScreens == m_showOnAllScreens) return; + m_showOnAllScreens = showOnAllScreens; + Q_EMIT showOnAllScreensChanged(m_showOnAllScreens); } else if (keyLocked == key) { auto locked = m_dockConfig->value(keyLocked).toBool(); if (locked == m_locked) return; @@ -379,6 +387,20 @@ bool DockSettings::showInPrimary() const return m_showInPrimary; } +bool DockSettings::showOnAllScreens() const +{ + return m_showOnAllScreens; +} + +void DockSettings::setShowOnAllScreens(bool newShowOnAllScreens) +{ + if (m_showOnAllScreens == newShowOnAllScreens) + return; + m_showOnAllScreens = newShowOnAllScreens; + m_dockConfig->setValue(keyShowOnAllScreens, m_showOnAllScreens); + Q_EMIT showOnAllScreensChanged(m_showOnAllScreens); +} + bool DockSettings::locked() const { return m_locked; diff --git a/panels/dock/docksettings.h b/panels/dock/docksettings.h index 5d1bee1d1..3e1d515ee 100644 --- a/panels/dock/docksettings.h +++ b/panels/dock/docksettings.h @@ -28,6 +28,7 @@ class DockSettings : public QObject Q_PROPERTY(QVariantMap pluginsVisible READ pluginsVisible WRITE setPluginsVisible NOTIFY pluginsVisibleChanged FINAL) Q_PROPERTY(QString cardCurrent READ cardCurrent WRITE setCardCurrent NOTIFY cardCurrentChanged FINAL) Q_PROPERTY(bool showInPrimary READ showInPrimary WRITE setShowInPrimary NOTIFY showInPrimaryChanged FINAL) + Q_PROPERTY(bool showOnAllScreens READ showOnAllScreens WRITE setShowOnAllScreens NOTIFY showOnAllScreensChanged FINAL) Q_PROPERTY(bool locked READ locked WRITE setLocked NOTIFY lockedChanged FINAL) Q_PROPERTY(bool contextMenuEnabled READ contextMenuEnabled NOTIFY contextMenuEnabledChanged FINAL) Q_PROPERTY(bool fashionModeEnabled READ fashionModeEnabled NOTIFY fashionModeEnabledChanged FINAL) @@ -43,6 +44,7 @@ class DockSettings : public QObject QVariantMap pluginsVisible(); QString cardCurrent() const; bool showInPrimary() const; + bool showOnAllScreens() const; bool locked() const; bool contextMenuEnabled() const; // 时尚模式是否允许开启,由 DConfig 的 enableFashionMode 控制,默认关闭 @@ -56,6 +58,7 @@ class DockSettings : public QObject void setPluginsVisible(const QVariantMap & pluginsVisible); void setCardCurrent(const QString &cardCurrent); void setShowInPrimary(bool newShowInPrimary); + void setShowOnAllScreens(bool newShowOnAllScreens); void setLocked(bool newLocked); private: @@ -84,6 +87,7 @@ class DockSettings : public QObject void cardCurrentChanged(const QString &cardCurrent); void showInPrimaryChanged(bool showInPrimary); + void showOnAllScreensChanged(bool showOnAllScreens); void lockedChanged(bool locked); void contextMenuEnabledChanged(bool enabled); void fashionModeEnabledChanged(bool enabled); @@ -104,6 +108,7 @@ class DockSettings : public QObject QVariantMap m_pluginsVisible; QString m_cardCurrent; bool m_showInPrimary; + bool m_showOnAllScreens; bool m_locked; bool m_contextMenuEnabled; bool m_fashionModeEnabled = false; diff --git a/shell/main.cpp b/shell/main.cpp index 51fc6a9ed..1657af090 100644 --- a/shell/main.cpp +++ b/shell/main.cpp @@ -5,9 +5,15 @@ #include #include #include +#include +#include +#include #include #include +#include +#include +#include #include #include #include @@ -18,6 +24,7 @@ #include #include "applet.h" +#include "appletdata.h" #include "containment.h" #include "pluginloader.h" #include "appletconfigmanager.h" @@ -26,6 +33,7 @@ #include "shell.h" DS_USE_NAMESPACE +DCORE_USE_NAMESPACE DGUI_USE_NAMESPACE DWIDGET_USE_NAMESPACE @@ -56,25 +64,34 @@ class AppletManager qCDebug(dsLog) << "Preloading plugins:" << pluginIds; Q_ASSERT(configManager); - auto rootApplet = qobject_cast(DPluginLoader::instance()->rootApplet()); - Q_ASSERT(rootApplet); + m_configManager = configManager; + Q_ASSERT(qobject_cast(DPluginLoader::instance()->rootApplet())); + + m_dockConfig.reset(DConfig::create("org.deepin.dde.shell", "org.deepin.ds.dock", QString(), qApp)); + if (m_dockConfig && m_dockConfig->isValid()) + m_showOnAllScreens = m_dockConfig->value(QStringLiteral("Show_On_All_Screens"), false).toBool(); for (const auto &pluginId : pluginIds) { - auto applet = rootApplet->createApplet(DAppletData{pluginId}); - if (!applet) { - qCWarning(dsLog) << "Loading plugin failed:" << pluginId; - continue; + if (pluginId == dockPluginId() && m_showOnAllScreens) { + for (auto *screen : qApp->screens()) + addDockForScreen(screen); + } else { + createLoader(DAppletData{pluginId}, pluginIds); } + } - auto loader = new DAppletLoader(applet, configManager); - m_loaders << loader; - QObject::connect(loader, &DAppletLoader::failed, qApp, [this, loader, pluginIds](const QString &pluginId) { - if (pluginIds.contains(pluginId)) { - m_loaders.removeOne(loader); - loader->deleteLater(); - } + if (m_dockConfig && m_dockConfig->isValid()) { + QObject::connect(m_dockConfig.data(), &DConfig::valueChanged, qApp, [this](const QString &key) { + if (key == QStringLiteral("Show_On_All_Screens")) + setShowOnAllScreens(m_dockConfig->value(key, false).toBool()); }); } + QObject::connect(qApp, &QGuiApplication::screenAdded, qApp, [this](QScreen *screen) { + scheduleDockForScreen(screen); + }); + QObject::connect(qApp, &QGuiApplication::screenRemoved, qApp, [this](QScreen *screen) { + removeDockForScreen(screen); + }); QObject::connect(configManager, &AppletConfigManager::appletEnabledChanged, @@ -104,6 +121,7 @@ class AppletManager for (auto loader : std::as_const(m_loaders)) { loader->exec(); } + m_started = true; } void quit() { @@ -132,6 +150,103 @@ class AppletManager } QList m_loaders; + +private: + static QString dockPluginId() { return QStringLiteral("org.deepin.ds.dock"); } + + void scheduleDockForScreen(QScreen *screen, int attempt = 0) + { + if (!screen || !m_showOnAllScreens) + return; + + QPointer pendingScreen(screen); + QTimer::singleShot(250, qApp, [this, pendingScreen, attempt] { + if (!pendingScreen || !m_showOnAllScreens || !qApp->screens().contains(pendingScreen)) + return; + + const auto geometry = pendingScreen->geometry(); + const bool geometryReady = geometry.width() > 1 && geometry.height() > 1; + if (!geometryReady && attempt < 20) { + scheduleDockForScreen(pendingScreen, attempt + 1); + return; + } + + if (geometryReady) + addDockForScreen(pendingScreen); + }); + } + + DAppletLoader *createLoader(const DAppletData &data, const QStringList &rootPluginIds) + { + auto rootApplet = qobject_cast(DPluginLoader::instance()->rootApplet()); + auto applet = rootApplet->createApplet(data); + if (!applet) { + qCWarning(dsLog) << "Loading plugin failed:" << data.pluginId(); + return nullptr; + } + auto loader = new DAppletLoader(applet, m_configManager); + m_loaders << loader; + QObject::connect(loader, &DAppletLoader::failed, qApp, [this, loader, rootPluginIds](const QString &pluginId) { + if (rootPluginIds.contains(pluginId)) { + m_loaders.removeOne(loader); + loader->deleteLater(); + } + }); + return loader; + } + + void addDockForScreen(QScreen *screen) + { + if (!screen || m_dockLoaders.contains(screen)) + return; + QVariantMap data; + data.insert(QStringLiteral("PluginId"), dockPluginId()); + data.insert(QStringLiteral("Id"), QUuid::createUuid().toString(QUuid::WithoutBraces)); + data.insert(QStringLiteral("DockMirror"), true); + data.insert(QStringLiteral("DockScreen"), screen->name()); + data.insert(QStringLiteral("DockMirrorPrimary"), screen == qApp->primaryScreen()); + if (auto *loader = createLoader(DAppletData(data), QStringList{dockPluginId()})) { + m_dockLoaders.insert(screen, loader); + if (m_started) + loader->exec(); + } + } + + void removeDockForScreen(QScreen *screen) + { + auto *loader = m_dockLoaders.take(screen); + if (!loader) + return; + if (auto *applet = loader->applet()) { + if (auto *rootApplet = qobject_cast(applet->parentApplet())) + rootApplet->removeApplet(applet); + } + m_loaders.removeOne(loader); + loader->deleteLater(); + } + + void setShowOnAllScreens(bool enabled) + { + if (m_showOnAllScreens == enabled) + return; + m_showOnAllScreens = enabled; + const auto screens = m_dockLoaders.keys(); + for (auto *screen : screens) + removeDockForScreen(screen); + if (m_showOnAllScreens) { + for (auto *screen : qApp->screens()) + addDockForScreen(screen); + } else { + if (auto *loader = createLoader(DAppletData{dockPluginId()}, QStringList{dockPluginId()})) + loader->exec(); + } + } + + QHash m_dockLoaders; + QScopedPointer m_dockConfig; + AppletConfigManager *m_configManager = nullptr; + bool m_showOnAllScreens = false; + bool m_started = false; }; int main(int argc, char *argv[])