@@ -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
50114DockHelper::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
206281void 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
237330void 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()
258354void 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
328455void DockHelper::checkNeedHideOrNot ()
329456{
457+ if (m_edgeWakeHoldTimer->isActive ()) {
458+ return ;
459+ }
460+
330461 bool needHide;
331462 switch (parent ()->hideMode ()) {
332463 case KeepShowing: {
0 commit comments