Skip to content

Commit b36f6f6

Browse files
committed
Tray workarounds #6545
* Disentangle the previous 'qdbusWorkarounds' into three different things * Make not trusting tray.isVisible() a new workaround * Introduce env vars for all workaround flags * Use the workaround flags for OSX * Determine workaround flags for KDE when the plasma integration plugin is missing
1 parent 0062a28 commit b36f6f6

File tree

2 files changed

+127
-58
lines changed

2 files changed

+127
-58
lines changed

src/gui/owncloudgui.cpp

+117-53
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,8 @@ ownCloudGui::ownCloudGui(Application *parent)
5959
,
6060
#endif
6161
_logBrowser(0)
62-
, _contextMenuVisibleOsx(false)
62+
, _contextMenuVisibleManual(false)
6363
, _recentActionsMenu(0)
64-
, _qdbusmenuWorkaround(false)
6564
, _app(parent)
6665
{
6766
_tray = new Systray();
@@ -117,7 +116,7 @@ void ownCloudGui::slotOpenSettingsDialog()
117116

118117
void ownCloudGui::slotTrayClicked(QSystemTrayIcon::ActivationReason reason)
119118
{
120-
if (_qdbusmenuWorkaround) {
119+
if (_workaroundFakeDoubleClick) {
121120
static QElapsedTimer last_click;
122121
if (last_click.isValid() && last_click.elapsed() < 200) {
123122
return;
@@ -402,29 +401,31 @@ void ownCloudGui::addAccountContextMenu(AccountStatePtr accountState, QMenu *men
402401

403402
void ownCloudGui::slotContextMenuAboutToShow()
404403
{
405-
// For some reason on OS X _contextMenu->isVisible returns always false
406-
_contextMenuVisibleOsx = true;
404+
_contextMenuVisibleManual = true;
407405

408406
// Update icon in sys tray, as it might change depending on the context menu state
409407
slotComputeOverallSyncStatus();
408+
409+
if (!_workaroundNoAboutToShowUpdate) {
410+
updateContextMenu();
411+
}
410412
}
411413

412414
void ownCloudGui::slotContextMenuAboutToHide()
413415
{
414-
// For some reason on OS X _contextMenu->isVisible returns always false
415-
_contextMenuVisibleOsx = false;
416+
_contextMenuVisibleManual = false;
416417

417418
// Update icon in sys tray, as it might change depending on the context menu state
418419
slotComputeOverallSyncStatus();
419420
}
420421

421422
bool ownCloudGui::contextMenuVisible() const
422423
{
423-
#ifdef Q_OS_MAC
424-
return _contextMenuVisibleOsx;
425-
#else
424+
// On some platforms isVisible doesn't work and always returns false,
425+
// elsewhere aboutToHide is unreliable.
426+
if (_workaroundManualVisibility)
427+
return _contextMenuVisibleManual;
426428
return _contextMenu->isVisible();
427-
#endif
428429
}
429430

430431
static bool minimalTrayMenu()
@@ -447,12 +448,36 @@ static bool updateWhileVisible()
447448
}
448449
}
449450

450-
static QByteArray forceQDBusTrayWorkaround()
451+
static QByteArray envForceQDBusTrayWorkaround()
451452
{
452453
static QByteArray var = qgetenv("OWNCLOUD_FORCE_QDBUS_TRAY_WORKAROUND");
453454
return var;
454455
}
455456

457+
static QByteArray envForceWorkaroundShowAndHideTray()
458+
{
459+
static QByteArray var = qgetenv("OWNCLOUD_FORCE_TRAY_SHOW_HIDE");
460+
return var;
461+
}
462+
463+
static QByteArray envForceWorkaroundNoAboutToShowUpdate()
464+
{
465+
static QByteArray var = qgetenv("OWNCLOUD_FORCE_TRAY_NO_ABOUT_TO_SHOW");
466+
return var;
467+
}
468+
469+
static QByteArray envForceWorkaroundFakeDoubleClick()
470+
{
471+
static QByteArray var = qgetenv("OWNCLOUD_FORCE_TRAY_FAKE_DOUBLE_CLICK");
472+
return var;
473+
}
474+
475+
static QByteArray envForceWorkaroundManualVisibility()
476+
{
477+
static QByteArray var = qgetenv("OWNCLOUD_FORCE_TRAY_MANUAL_VISIBILITY");
478+
return var;
479+
}
480+
456481
void ownCloudGui::setupContextMenu()
457482
{
458483
if (_contextMenu) {
@@ -475,6 +500,8 @@ void ownCloudGui::setupContextMenu()
475500
return;
476501
}
477502

503+
bool qdbusmenuWorkarounds = false;
504+
478505
// Enables workarounds for bugs introduced in Qt 5.5.0
479506
// In particular QTBUG-47863 #3672 (tray menu fails to update and
480507
// becomes unresponsive) and QTBUG-48068 #3722 (click signal is
@@ -490,36 +517,70 @@ void ownCloudGui::setupContextMenu()
490517
QObject *platformMenu = reinterpret_cast<QObject *>(_tray->contextMenu()->platformMenu());
491518
if (platformMenu
492519
&& platformMenu->metaObject()->className() == QLatin1String("QDBusPlatformMenu")) {
493-
_qdbusmenuWorkaround = true;
520+
qdbusmenuWorkarounds = true;
494521
qCWarning(lcApplication) << "Enabled QDBusPlatformMenu workaround";
495522
}
496523
}
497524
#endif
498525
#endif
499526

500-
if (forceQDBusTrayWorkaround() == "1") {
501-
_qdbusmenuWorkaround = true;
502-
} else if (forceQDBusTrayWorkaround() == "0") {
503-
_qdbusmenuWorkaround = false;
527+
auto applyEnvVariable = [](bool *sw, const QByteArray &value) {
528+
if (value == "1")
529+
*sw = true;
530+
if (value == "0")
531+
*sw = false;
532+
};
533+
534+
applyEnvVariable(&qdbusmenuWorkarounds, envForceQDBusTrayWorkaround());
535+
if (qdbusmenuWorkarounds) {
536+
_workaroundFakeDoubleClick = true;
537+
_workaroundNoAboutToShowUpdate = true;
538+
_workaroundShowAndHideTray = true;
504539
}
505540

506-
// When the qdbusmenuWorkaround is necessary, we can't do on-demand updates
507-
// because the workaround is to hide and show the tray icon.
508-
if (_qdbusmenuWorkaround) {
509-
connect(&_workaroundBatchTrayUpdate, &QTimer::timeout, this, &ownCloudGui::updateContextMenu);
510-
_workaroundBatchTrayUpdate.setInterval(30 * 1000);
511-
_workaroundBatchTrayUpdate.setSingleShot(true);
512-
} else {
513-
// Update the context menu whenever we're about to show it
514-
// to the user.
515541
#ifdef Q_OS_MAC
516-
// https://bugreports.qt.io/browse/QTBUG-54633
517-
connect(_contextMenu.data(), SIGNAL(aboutToShow()), SLOT(slotContextMenuAboutToShow()));
518-
connect(_contextMenu.data(), SIGNAL(aboutToHide()), SLOT(slotContextMenuAboutToHide()));
519-
#else
520-
connect(_contextMenu.data(), &QMenu::aboutToShow, this, &ownCloudGui::updateContextMenu);
542+
// https://bugreports.qt.io/browse/QTBUG-54633
543+
_workaroundNoAboutToShowUpdate = true;
544+
_workaroundManualVisibility = true;
521545
#endif
546+
547+
#ifdef Q_OS_LINUX
548+
// For KDE sessions if the platform plugin is missing,
549+
// neither aboutToShow() updates nor the isVisible() call
550+
// work. At least aboutToHide is reliable.
551+
// https://github.com/owncloud/client/issues/6545
552+
static QByteArray xdgCurrentDesktop = qgetenv("XDG_CURRENT_DESKTOP");
553+
static QByteArray desktopSession = qgetenv("DESKTOP_SESSION");
554+
bool isKde =
555+
xdgCurrentDesktop.contains("KDE")
556+
|| desktopSession.contains("plasma")
557+
|| desktopSession.contains("kde");
558+
QObject *platformMenu = reinterpret_cast<QObject *>(_tray->contextMenu()->platformMenu());
559+
if (isKde && platformMenu && platformMenu->metaObject()->className() == QLatin1String("QDBusPlatformMenu")) {
560+
_workaroundManualVisibility = true;
561+
_workaroundNoAboutToShowUpdate = true;
522562
}
563+
#endif
564+
565+
applyEnvVariable(&_workaroundNoAboutToShowUpdate, envForceWorkaroundNoAboutToShowUpdate());
566+
applyEnvVariable(&_workaroundFakeDoubleClick, envForceWorkaroundFakeDoubleClick());
567+
applyEnvVariable(&_workaroundShowAndHideTray, envForceWorkaroundShowAndHideTray());
568+
applyEnvVariable(&_workaroundManualVisibility, envForceWorkaroundManualVisibility());
569+
570+
qCDebug(lcApplication) << "Tray menu workarounds: "
571+
<< "noabouttoshow: " << _workaroundNoAboutToShowUpdate
572+
<< "fakedoubleclick: " << _workaroundFakeDoubleClick
573+
<< "showhide: " << _workaroundShowAndHideTray
574+
<< "manualvisibility: " << _workaroundManualVisibility;
575+
576+
577+
connect(&_delayedTrayUpdateTimer, &QTimer::timeout, this, &ownCloudGui::updateContextMenu);
578+
_delayedTrayUpdateTimer.setInterval(2 * 1000);
579+
_delayedTrayUpdateTimer.setSingleShot(true);
580+
581+
connect(_contextMenu.data(), SIGNAL(aboutToShow()), SLOT(slotContextMenuAboutToShow()));
582+
// unfortunately aboutToHide is unreliable, it seems to work on OSX though
583+
connect(_contextMenu.data(), SIGNAL(aboutToHide()), SLOT(slotContextMenuAboutToHide()));
523584

524585
// Populate the context menu now.
525586
updateContextMenu();
@@ -531,13 +592,21 @@ void ownCloudGui::updateContextMenu()
531592
return;
532593
}
533594

534-
if (_qdbusmenuWorkaround) {
595+
// If it's visible, we can't update live, and it won't be updated lazily: reschedule
596+
if (contextMenuVisible() && !updateWhileVisible() && _workaroundNoAboutToShowUpdate) {
597+
if (!_delayedTrayUpdateTimer.isActive()) {
598+
_delayedTrayUpdateTimer.start();
599+
}
600+
return;
601+
}
602+
603+
if (_workaroundShowAndHideTray) {
535604
// To make tray menu updates work with these bugs (see setupContextMenu)
536605
// we need to hide and show the tray icon. We don't want to do that
537606
// while it's visible!
538607
if (contextMenuVisible()) {
539-
if (!_workaroundBatchTrayUpdate.isActive()) {
540-
_workaroundBatchTrayUpdate.start();
608+
if (!_delayedTrayUpdateTimer.isActive()) {
609+
_delayedTrayUpdateTimer.start();
541610
}
542611
return;
543612
}
@@ -652,35 +721,30 @@ void ownCloudGui::updateContextMenu()
652721
}
653722
_contextMenu->addAction(_actionQuit);
654723

655-
if (_qdbusmenuWorkaround) {
724+
if (_workaroundShowAndHideTray) {
656725
_tray->show();
657726
}
658727
}
659728

660729
void ownCloudGui::updateContextMenuNeeded()
661730
{
662-
// For the workaround case updating while visible is impossible. Instead
663-
// occasionally update the menu when it's invisible.
664-
if (_qdbusmenuWorkaround) {
665-
if (!_workaroundBatchTrayUpdate.isActive()) {
666-
_workaroundBatchTrayUpdate.start();
667-
}
731+
// if it's visible and we can update live: update now
732+
if (contextMenuVisible() && updateWhileVisible()) {
733+
// Note: don't update while visible on OSX
734+
// https://bugreports.qt.io/browse/QTBUG-54845
735+
updateContextMenu();
668736
return;
669737
}
670738

671-
#ifdef Q_OS_MAC
672-
// https://bugreports.qt.io/browse/QTBUG-54845
673-
// We cannot update on demand or while visible -> update when invisible.
674-
if (!contextMenuVisible()) {
675-
updateContextMenu();
739+
// if we can't lazily update: update later
740+
if (_workaroundNoAboutToShowUpdate) {
741+
// Note: don't update immediately even in the invisible case
742+
// as that can lead to extremely frequent menu updates
743+
if (!_delayedTrayUpdateTimer.isActive()) {
744+
_delayedTrayUpdateTimer.start();
745+
}
746+
return;
676747
}
677-
#else
678-
if (updateWhileVisible() && contextMenuVisible())
679-
updateContextMenu();
680-
#endif
681-
682-
// If no update was done here, we might update it on-demand due to
683-
// the aboutToShow() signal.
684748
}
685749

686750
void ownCloudGui::slotShowTrayMessage(const QString &title, const QString &msg)

src/gui/owncloudgui.h

+10-5
Original file line numberDiff line numberDiff line change
@@ -124,14 +124,19 @@ private slots:
124124
// tray's menu
125125
QScopedPointer<QMenu> _contextMenu;
126126

127-
// Manually tracking whether the context menu is visible, but only works
128-
// on OSX because aboutToHide is not reliable everywhere.
129-
bool _contextMenuVisibleOsx;
127+
// Manually tracking whether the context menu is visible via aboutToShow
128+
// and aboutToHide. Unfortunately aboutToHide isn't reliable everywhere
129+
// so this only gets used with _workaroundManualVisibility (when the tray's
130+
// isVisible() is unreliable)
131+
bool _contextMenuVisibleManual;
130132

131133
QMenu *_recentActionsMenu;
132134
QVector<QMenu *> _accountMenus;
133-
bool _qdbusmenuWorkaround;
134-
QTimer _workaroundBatchTrayUpdate;
135+
bool _workaroundShowAndHideTray = false;
136+
bool _workaroundNoAboutToShowUpdate = false;
137+
bool _workaroundFakeDoubleClick = false;
138+
bool _workaroundManualVisibility = false;
139+
QTimer _delayedTrayUpdateTimer;
135140
QMap<QString, QPointer<ShareDialog>> _shareDialogs;
136141

137142
QAction *_actionLogin;

0 commit comments

Comments
 (0)