Skip to content

Commit 2e77be2

Browse files
committed
fix(dock): refine fashion mode behavior and assets
Refine the fashion mode behavior after the initial feature landing. Adjust the related assets so the updated interactions render correctly.
1 parent b8a81b1 commit 2e77be2

32 files changed

Lines changed: 640 additions & 186 deletions

panels/dock/dockhelper.cpp

Lines changed: 135 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,86 @@ QRect expandedGeometry(const QRect &geometry, int margin)
4545
return geometry.adjusted(-margin, -margin, margin, margin);
4646
}
4747

48+
QRect dockMouseTrackingGeometry(const QRect &geometry, const QRect &screenGeometry, Position position, int margin)
49+
{
50+
QRect trackingGeometry = expandedGeometry(geometry, margin);
51+
52+
const int leftGap = qMax(0, geometry.left() - screenGeometry.left());
53+
const int topGap = qMax(0, geometry.top() - screenGeometry.top());
54+
const int rightGap = qMax(0, screenGeometry.right() - geometry.right());
55+
const int bottomGap = qMax(0, screenGeometry.bottom() - geometry.bottom());
56+
57+
switch (position) {
58+
case Bottom:
59+
trackingGeometry.adjust(0, 0, 0, qMax(0, bottomGap - margin));
60+
break;
61+
case Top:
62+
trackingGeometry.adjust(0, -qMax(0, topGap - margin), 0, 0);
63+
break;
64+
case Left:
65+
trackingGeometry.adjust(-qMax(0, leftGap - margin), 0, 0, 0);
66+
break;
67+
case Right:
68+
trackingGeometry.adjust(0, 0, qMax(0, rightGap - margin), 0);
69+
break;
70+
}
71+
72+
return trackingGeometry;
73+
}
74+
75+
bool isCursorOnDockWakeEdge(const QPoint &globalCursorPos, const QRect &screenGeometry, Position position)
76+
{
77+
if (!screenGeometry.isValid()) {
78+
return false;
79+
}
80+
81+
constexpr int edgeThreshold = 2;
82+
if (!screenGeometry.adjusted(-edgeThreshold, -edgeThreshold, edgeThreshold, edgeThreshold).contains(globalCursorPos)) {
83+
return false;
84+
}
85+
86+
switch (position) {
87+
case Bottom:
88+
return globalCursorPos.y() >= screenGeometry.bottom() - edgeThreshold;
89+
case Top:
90+
return globalCursorPos.y() <= screenGeometry.top() + edgeThreshold;
91+
case Left:
92+
return globalCursorPos.x() <= screenGeometry.left() + edgeThreshold;
93+
case Right:
94+
return globalCursorPos.x() >= screenGeometry.right() - edgeThreshold;
95+
}
96+
97+
return false;
98+
}
99+
100+
bool shouldUseCursorEdgeWakeFallback(DockPanel *panel)
101+
{
102+
if (!panel) {
103+
return false;
104+
}
105+
106+
// Fashion mode uses its own wake-up surface. Keeping the generic edge fallback
107+
// enabled there causes premature wake-ups and repeated hide/show loops while
108+
// the cursor is still parked near the screen edge.
109+
return panel->viewMode() != FashionMode;
110+
}
111+
48112
}
49113

50114
DockHelper::DockHelper(DockPanel *parent)
51115
: QObject(parent)
52116
, m_hideTimer(new QTimer(this))
53117
, m_showTimer(new QTimer(this))
54118
, m_cursorMonitorTimer(new QTimer(this))
119+
, m_edgeWakeHoldTimer(new QTimer(this))
55120
{
56121
m_hideTimer->setInterval(400);
57-
m_showTimer->setInterval(400);
122+
m_showTimer->setInterval(120);
58123
m_cursorMonitorTimer->setInterval(16);
124+
m_edgeWakeHoldTimer->setInterval(420);
59125
m_hideTimer->setSingleShot(true);
60126
m_showTimer->setSingleShot(true);
127+
m_edgeWakeHoldTimer->setSingleShot(true);
61128

62129
qApp->installEventFilter(this);
63130
QMetaObject::invokeMethod(this, &DockHelper::initAreas, Qt::QueuedConnection);
@@ -66,6 +133,10 @@ DockHelper::DockHelper(DockPanel *parent)
66133
connect(parent, &DockPanel::rootObjectChanged, this, &DockHelper::initAreas);
67134
connect(parent, &DockPanel::showInPrimaryChanged, this, &DockHelper::updateAllDockWakeArea);
68135
connect(parent, &DockPanel::hideStateChanged, this, &DockHelper::updateAllDockWakeArea);
136+
connect(parent, &DockPanel::viewModeChanged, this, [this]() {
137+
updateAllDockWakeArea();
138+
updatePanelMouseState();
139+
});
69140
connect(parent, &DockPanel::hideModeChanged, m_hideTimer, static_cast<void (QTimer::*)()>(&QTimer::start));
70141
connect(parent, &DockPanel::hideModeChanged, m_showTimer, static_cast<void (QTimer::*)()>(&QTimer::start));
71142
connect(parent, &DockPanel::positionChanged, this, [this](Position pos) {
@@ -79,6 +150,7 @@ DockHelper::DockHelper(DockPanel *parent)
79150
connect(m_hideTimer, &QTimer::timeout, this, &DockHelper::checkNeedHideOrNot);
80151
connect(m_showTimer, &QTimer::timeout, this, &DockHelper::checkNeedShowOrNot);
81152
connect(m_cursorMonitorTimer, &QTimer::timeout, this, &DockHelper::updatePanelMouseState);
153+
connect(m_edgeWakeHoldTimer, &QTimer::timeout, this, &DockHelper::checkNeedHideOrNot);
82154
m_cursorMonitorTimer->start();
83155

84156
connect(this, &DockHelper::isWindowOverlapChanged, this, [this](bool overlap) {
@@ -141,6 +213,9 @@ bool DockHelper::eventFilter(QObject *watched, QEvent *event)
141213
case QEvent::Enter: {
142214
m_enters.insert(window, true);
143215
updateCursorPosition(event);
216+
if (m_edgeWakeHoldTimer->isActive()) {
217+
m_edgeWakeHoldTimer->stop();
218+
}
144219
if (m_hideTimer->isActive()) {
145220
m_hideTimer->stop();
146221
}
@@ -205,6 +280,17 @@ bool DockHelper::wakeUpAreaNeedShowOnThisScreen(QScreen *screen)
205280

206281
void DockHelper::enterScreen(QScreen *screen)
207282
{
283+
if (!screen) {
284+
return;
285+
}
286+
287+
if (m_edgeWakeLatched && m_edgeWakeScreen == screen) {
288+
return;
289+
}
290+
291+
m_edgeWakeLatched = true;
292+
m_edgeWakeScreen = screen;
293+
208294
if (m_hideTimer->isActive()) {
209295
m_hideTimer->stop();
210296
}
@@ -215,19 +301,26 @@ void DockHelper::enterScreen(QScreen *screen)
215301
auto nowScreen = parent()->dockScreen();
216302

217303
if (nowScreen == screen) {
304+
m_edgeWakeHoldTimer->start();
218305
parent()->setHideState(Show);
219306
return;
220307
}
221308

222309
// Do not switch screen if any popup/transient child window is showing
223310
for (auto show : m_transientChildShows) {
224311
if (show) {
312+
m_edgeWakeHoldTimer->start();
225313
parent()->setHideState(Show);
226314
return;
227315
}
228316
}
229317

230318
QTimer::singleShot(200, [this, screen]() {
319+
if (!screen) {
320+
return;
321+
}
322+
323+
m_edgeWakeHoldTimer->start();
231324
parent()->setDockScreen(screen);
232325
parent()->setHideState(Show);
233326
updateAllDockWakeArea();
@@ -236,6 +329,9 @@ void DockHelper::enterScreen(QScreen *screen)
236329

237330
void DockHelper::leaveScreen()
238331
{
332+
if (m_edgeWakeHoldTimer->isActive()) {
333+
m_edgeWakeHoldTimer->stop();
334+
}
239335
m_hideTimer->start();
240336
}
241337

@@ -258,14 +354,39 @@ void DockHelper::updateAllDockWakeArea()
258354
void DockHelper::updatePanelMouseState()
259355
{
260356
auto *window = parent()->window();
357+
const QPoint globalCursorPos = QCursor::pos();
358+
QScreen *cursorScreen = QGuiApplication::screenAt(globalCursorPos);
359+
if (!cursorScreen && window) {
360+
cursorScreen = window->screen();
361+
}
362+
363+
const bool cursorOnWakeEdge = cursorScreen
364+
&& isCursorOnDockWakeEdge(globalCursorPos, cursorScreen->geometry(), parent()->position());
365+
366+
if (!cursorScreen
367+
|| cursorScreen != m_edgeWakeScreen
368+
|| !cursorOnWakeEdge) {
369+
m_edgeWakeLatched = false;
370+
m_edgeWakeScreen.clear();
371+
}
372+
373+
if (shouldUseCursorEdgeWakeFallback(parent())
374+
&& parent()->hideState() == Hide
375+
&& cursorScreen
376+
&& (!parent()->showInPrimary() || cursorScreen == qApp->primaryScreen())
377+
&& cursorOnWakeEdge) {
378+
enterScreen(cursorScreen);
379+
}
380+
261381
if (!window || !window->isVisible()) {
262382
parent()->setContainsMouse(false);
263383
return;
264384
}
265385

266-
const QPoint globalCursorPos = QCursor::pos();
267386
const int geometryMargin = qMax(6, qRound(parent()->dockSize() * 0.14));
268-
bool containsMouse = expandedGeometry(window->geometry(), geometryMargin).contains(globalCursorPos);
387+
const QRect screenGeometry = window->screen() ? window->screen()->geometry() : QRect();
388+
const Position panelPosition = parent()->position();
389+
bool containsMouse = dockMouseTrackingGeometry(window->geometry(), screenGeometry, panelPosition, geometryMargin).contains(globalCursorPos);
269390

270391
if (!containsMouse) {
271392
const auto topLevelWindows = QGuiApplication::topLevelWindows();
@@ -274,7 +395,8 @@ void DockHelper::updatePanelMouseState()
274395
continue;
275396
}
276397

277-
if (expandedGeometry(candidateWindow->geometry(), geometryMargin).contains(globalCursorPos)) {
398+
const QRect candidateScreenGeometry = candidateWindow->screen() ? candidateWindow->screen()->geometry() : screenGeometry;
399+
if (dockMouseTrackingGeometry(candidateWindow->geometry(), candidateScreenGeometry, panelPosition, geometryMargin).contains(globalCursorPos)) {
278400
containsMouse = true;
279401
break;
280402
}
@@ -283,6 +405,11 @@ void DockHelper::updatePanelMouseState()
283405

284406
parent()->setContainsMouse(containsMouse);
285407
if (containsMouse) {
408+
m_edgeWakeLatched = false;
409+
m_edgeWakeScreen.clear();
410+
if (m_edgeWakeHoldTimer->isActive()) {
411+
m_edgeWakeHoldTimer->stop();
412+
}
286413
parent()->setCursorPosition(globalCursorPos - window->position());
287414
}
288415
}
@@ -327,6 +454,10 @@ void DockHelper::updateCursorPosition(QEvent *event)
327454

328455
void DockHelper::checkNeedHideOrNot()
329456
{
457+
if (m_edgeWakeHoldTimer->isActive()) {
458+
return;
459+
}
460+
330461
bool needHide;
331462
switch (parent()->hideMode()) {
332463
case KeepShowing: {

panels/dock/dockhelper.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
#include "dockpanel.h"
99

1010
#include <QObject>
11+
#include <QPointer>
12+
13+
class QTimer;
1114

1215
namespace dock {
1316
class DockWakeUpArea;
@@ -55,6 +58,9 @@ public Q_SLOTS:
5558
QTimer *m_hideTimer;
5659
QTimer *m_showTimer;
5760
QTimer *m_cursorMonitorTimer;
61+
QTimer *m_edgeWakeHoldTimer;
62+
bool m_edgeWakeLatched = false;
63+
QPointer<QScreen> m_edgeWakeScreen;
5864
};
5965

6066
class DockWakeUpArea
@@ -75,4 +81,3 @@ class DockWakeUpArea
7581
DockHelper *m_helper;
7682
};
7783
}
78-

panels/dock/dockpanel.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,13 @@ bool DockPanel::init()
326326
connect(this, &DockPanel::frontendWindowRectChanged, dockDaemonAdaptor, &DockDaemonAdaptor::FrontendWindowRectChanged);
327327
connect(SETTINGS, &DockSettings::dockSizeChanged, this, &DockPanel::dockSizeChanged);
328328
connect(SETTINGS, &DockSettings::hideModeChanged, this, &DockPanel::hideModeChanged);
329-
connect(SETTINGS, &DockSettings::viewModeChanged, this, &DockPanel::viewModeChanged);
329+
connect(SETTINGS, &DockSettings::viewModeChanged, this, [this](ViewMode mode) {
330+
Q_EMIT viewModeChanged(mode);
331+
332+
if (window()) {
333+
QMetaObject::invokeMethod(this, &DockPanel::onWindowGeometryChanged, Qt::QueuedConnection);
334+
}
335+
});
330336
connect(SETTINGS, &DockSettings::itemAlignmentChanged, this, &DockPanel::itemAlignmentChanged);
331337
connect(SETTINGS, &DockSettings::indicatorStyleChanged, this, &DockPanel::indicatorStyleChanged);
332338
connect(SETTINGS, &DockSettings::lockedChanged, this, &DockPanel::lockedChanged);

panels/dock/fashionleftpluginprovider.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ constexpr auto SystemMonitorMainInterface = "com.deepin.SystemMonitorMain";
8080
const QString WeatherAppIconPath = QStringLiteral("/usr/share/icons/hicolor/scalable/apps/org.deepin.weather.svg");
8181
const QString WeatherDockPluginPath = QStringLiteral("/usr/lib/dde-dock/plugins/system-trays/libdeepin-weather-dock-plugin.so");
8282
const QString MessageIconPath = QStringLiteral("/usr/share/icons/bloom/actions/24/mail-unread-new.svg");
83+
const QString DockPackageDataPath = QStringLiteral("/usr/share/dde-shell/org.deepin.ds.dock");
8384
const QString DefaultMailIconName = QStringLiteral("deepin-mail");
8485
const QString DefaultMusicIconName = QStringLiteral("audio-x-generic");
8586
constexpr int WeatherPopupTaskbarGap = 10;
@@ -2341,7 +2342,23 @@ WeatherCodeToIconNameFunction weatherCodeToIconNameFunction()
23412342

23422343
QString weatherAssetPath(const QString &assetName)
23432344
{
2345+
const auto packageAssetPath = [assetName]() {
2346+
if (assetName.isEmpty()) {
2347+
return QString();
2348+
}
2349+
2350+
return firstExistingPath({
2351+
QDir(QGuiApplication::applicationDirPath()).filePath(
2352+
QStringLiteral("../share/dde-shell/org.deepin.ds.dock/icons/%1.svg").arg(assetName)),
2353+
QStringLiteral("%1/icons/%2.svg").arg(DockPackageDataPath, assetName),
2354+
});
2355+
};
2356+
23442357
return firstExistingPath({
2358+
// Keep the fashion weather iconography stable across machines.
2359+
// Some systems only provide symbolic status weather icons, which look
2360+
// like enlarged tray icons when rendered in the left fashion panel.
2361+
packageAssetPath(),
23452362
QStringLiteral("/usr/share/icons/Win11/status/32/%1.svg").arg(assetName),
23462363
QStringLiteral("/usr/share/icons/Win11/status/16/%1.svg").arg(assetName),
23472364
QStringLiteral("/usr/share/icons/Adwaita/symbolic/status/%1-symbolic.svg").arg(assetName),

panels/dock/package/DockPartAppletModel.qml

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ D.SortFilterModel {
5454
"org.deepin.ds.dock.multitaskview"
5555
].indexOf(pluginId) >= 0
5656
readonly property int unifiedHoverBackgroundSize: Math.round((Panel.rootObject ? Panel.rootObject.dockItemMaxSize * 9 / 14 : 0) + 8)
57+
implicitWidth: appletItem ? appletItem.implicitWidth : 0
58+
implicitHeight: appletItem ? appletItem.implicitHeight : 0
5759

5860
contentItem: appletItem
5961
background: AppletItemBackground {
@@ -77,6 +79,19 @@ D.SortFilterModel {
7779
enabled: delegateRoot.useUnifiedDockHoverBackground && delegateRoot.visible
7880
}
7981

82+
Timer {
83+
id: syncAppletItemTimer
84+
interval: 0
85+
repeat: false
86+
onTriggered: delegateRoot.syncAppletItem()
87+
}
88+
89+
function scheduleSyncAppletItem() {
90+
if (appletItem) {
91+
syncAppletItemTimer.restart()
92+
}
93+
}
94+
8095
function syncAppletItem() {
8196
if (attachedAppletItem && attachedAppletItem !== appletItem && attachedAppletItem.parent === delegateRoot) {
8297
attachedAppletItem.parent = null
@@ -90,7 +105,13 @@ D.SortFilterModel {
90105
}
91106
}
92107

93-
onAppletItemChanged: syncAppletItem()
108+
onAppletItemChanged: {
109+
syncAppletItem()
110+
scheduleSyncAppletItem()
111+
}
112+
onVisibleChanged: scheduleSyncAppletItem()
113+
onWindowChanged: scheduleSyncAppletItem()
114+
onParentChanged: scheduleSyncAppletItem()
94115

95116
Component.onCompleted: syncAppletItem()
96117

@@ -100,5 +121,15 @@ D.SortFilterModel {
100121
}
101122
attachedAppletItem = null
102123
}
124+
125+
Connections {
126+
target: Panel
127+
128+
function onHideStateChanged() {
129+
if (Panel.hideState !== Dock.Hide) {
130+
delegateRoot.scheduleSyncAppletItem()
131+
}
132+
}
133+
}
103134
}
104135
}

0 commit comments

Comments
 (0)