Improve behavior under Wayland

* Add workaround for using QCursor::pos()
* Set window flags and parent of TrayMenu to it is shown
  like a context menu
This commit is contained in:
Martchus 2019-06-22 17:28:09 +02:00
parent 9f9b9124a4
commit 1bdba31c70
11 changed files with 207 additions and 53 deletions

View File

@ -111,7 +111,7 @@ void trigger(bool tray, bool webUi)
trayWidget->showWebUi(); trayWidget->showWebUi();
} }
if (tray) { if (tray) {
trayWidget->showAtCursor(); trayWidget->showUsingPositioningSettings();
} }
} }

View File

@ -35,7 +35,7 @@ namespace QtGui {
*/ */
TrayIcon::TrayIcon(const QString &connectionConfig, QObject *parent) TrayIcon::TrayIcon(const QString &connectionConfig, QObject *parent)
: QSystemTrayIcon(parent) : QSystemTrayIcon(parent)
, m_trayMenu(this) , m_trayMenu(new TrayMenu(this, &m_parentWidget))
#ifdef QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS #ifdef QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS
, m_dbusNotificationsEnabled(Settings::values().dbusNotifications) , m_dbusNotificationsEnabled(Settings::values().dbusNotifications)
#endif #endif
@ -43,7 +43,7 @@ TrayIcon::TrayIcon(const QString &connectionConfig, QObject *parent)
, m_messageClickedAction(TrayIconMessageClickedAction::None) , m_messageClickedAction(TrayIconMessageClickedAction::None)
{ {
// get widget, connection and notifier // get widget, connection and notifier
const auto &widget(m_trayMenu.widget()); const auto &widget(trayMenu().widget());
const auto &connection(widget.connection()); const auto &connection(widget.connection());
const auto &notifier(widget.notifier()); const auto &notifier(widget.notifier());
@ -71,7 +71,7 @@ TrayIcon::TrayIcon(const QString &connectionConfig, QObject *parent)
tr("Show internal errors")); tr("Show internal errors"));
m_errorsAction->setVisible(false); m_errorsAction->setVisible(false);
connect(m_errorsAction, &QAction::triggered, this, &TrayIcon::showInternalErrorsDialog); connect(m_errorsAction, &QAction::triggered, this, &TrayIcon::showInternalErrorsDialog);
m_contextMenu.addMenu(m_trayMenu.widget().connectionsMenu()); m_contextMenu.addMenu(trayMenu().widget().connectionsMenu());
connect(m_contextMenu.addAction( connect(m_contextMenu.addAction(
QIcon::fromTheme(QStringLiteral("help-about"), QIcon(QStringLiteral(":/icons/hicolor/scalable/apps/help-about.svg"))), tr("About")), QIcon::fromTheme(QStringLiteral("help-about"), QIcon(QStringLiteral(":/icons/hicolor/scalable/apps/help-about.svg"))), tr("About")),
&QAction::triggered, &widget, &TrayWidget::showAboutDialog); &QAction::triggered, &widget, &TrayWidget::showAboutDialog);
@ -109,7 +109,7 @@ TrayIcon::TrayIcon(const QString &connectionConfig, QObject *parent)
// apply settings, this also establishes the connection to Syncthing (according to settings) // apply settings, this also establishes the connection to Syncthing (according to settings)
// note: it is important to apply settings after all Signals & Slots have been connected (eg. to handle SyncthingConnection::error()) // note: it is important to apply settings after all Signals & Slots have been connected (eg. to handle SyncthingConnection::error())
m_trayMenu.widget().applySettings(connectionConfig); trayMenu().widget().applySettings(connectionConfig);
} }
/*! /*!
@ -136,10 +136,10 @@ void TrayIcon::handleActivated(QSystemTrayIcon::ActivationReason reason)
// can't catch that event on Plasma 5 anyways // can't catch that event on Plasma 5 anyways
break; break;
case QSystemTrayIcon::MiddleClick: case QSystemTrayIcon::MiddleClick:
m_trayMenu.widget().showWebUi(); trayMenu().widget().showWebUi();
break; break;
case QSystemTrayIcon::Trigger: { case QSystemTrayIcon::Trigger: {
m_trayMenu.showAtCursor(); trayMenu().showUsingPositioningSettings();
break; break;
} }
default:; default:;
@ -152,13 +152,13 @@ void TrayIcon::handleMessageClicked()
case TrayIconMessageClickedAction::None: case TrayIconMessageClickedAction::None:
return; return;
case TrayIconMessageClickedAction::DismissNotification: case TrayIconMessageClickedAction::DismissNotification:
m_trayMenu.widget().dismissNotifications(); trayMenu().widget().dismissNotifications();
break; break;
case TrayIconMessageClickedAction::ShowInternalErrors: case TrayIconMessageClickedAction::ShowInternalErrors:
showInternalErrorsDialog(); showInternalErrorsDialog();
break; break;
case TrayIconMessageClickedAction::ShowWebUi: case TrayIconMessageClickedAction::ShowWebUi:
m_trayMenu.widget().showWebUi(); trayMenu().widget().showWebUi();
break; break;
} }
} }
@ -199,7 +199,7 @@ void TrayIcon::handleErrorsCleared()
void TrayIcon::showInternalError( void TrayIcon::showInternalError(
const QString &errorMessage, SyncthingErrorCategory category, int networkError, const QNetworkRequest &request, const QByteArray &response) const QString &errorMessage, SyncthingErrorCategory category, int networkError, const QNetworkRequest &request, const QByteArray &response)
{ {
if (!InternalError::isRelevant(m_trayMenu.widget().connection(), category, networkError)) { if (!InternalError::isRelevant(trayMenu().widget().connection(), category, networkError)) {
return; return;
} }
InternalError error(errorMessage, request.url(), response); InternalError error(errorMessage, request.url(), response);

View File

@ -49,7 +49,8 @@ private slots:
void handleErrorsCleared(); void handleErrorsCleared();
private: private:
TrayMenu m_trayMenu; QWidget m_parentWidget;
TrayMenu *m_trayMenu;
#ifndef SYNCTHINGTRAY_UNIFY_TRAY_MENUS #ifndef SYNCTHINGTRAY_UNIFY_TRAY_MENUS
QMenu m_contextMenu; QMenu m_contextMenu;
QAction *m_errorsAction; QAction *m_errorsAction;
@ -64,7 +65,7 @@ private:
inline TrayMenu &TrayIcon::trayMenu() inline TrayMenu &TrayIcon::trayMenu()
{ {
return m_trayMenu; return *m_trayMenu;
} }
} // namespace QtGui } // namespace QtGui

View File

@ -7,7 +7,6 @@
#include <qtutilities/misc/dialogutils.h> #include <qtutilities/misc/dialogutils.h>
#include <QApplication> #include <QApplication>
#include <QCursor>
#include <QDesktopWidget> #include <QDesktopWidget>
#include <QHBoxLayout> #include <QHBoxLayout>
@ -25,6 +24,7 @@ TrayMenu::TrayMenu(TrayIcon *trayIcon, QWidget *parent)
menuLayout->addWidget(m_trayWidget = new TrayWidget(this)); menuLayout->addWidget(m_trayWidget = new TrayWidget(this));
setLayout(menuLayout); setLayout(menuLayout);
setPlatformMenu(nullptr); setPlatformMenu(nullptr);
setWindowFlags(Qt::ToolTip | Qt::FramelessWindowHint | Qt::Popup);
} }
QSize TrayMenu::sizeHint() const QSize TrayMenu::sizeHint() const
@ -50,10 +50,10 @@ void moveInside(QPoint &point, const QSize &innerRect, const QRect &outerRect)
} }
} }
void TrayMenu::showAtCursor() void TrayMenu::showUsingPositioningSettings()
{ {
resize(sizeHint()); resize(sizeHint());
QPoint pos(QCursor::pos()); auto pos = Settings::values().appearance.positioning.positionToUse();
moveInside(pos, size(), availableScreenGeometryAtPoint(pos)); moveInside(pos, size(), availableScreenGeometryAtPoint(pos));
popup(pos); popup(pos);
} }

View File

@ -20,7 +20,7 @@ public:
TrayIcon *icon(); TrayIcon *icon();
public slots: public slots:
void showAtCursor(); void showUsingPositioningSettings();
private: private:
TrayWidget *m_trayWidget; TrayWidget *m_trayWidget;

View File

@ -36,7 +36,6 @@
#include <QClipboard> #include <QClipboard>
#include <QCoreApplication> #include <QCoreApplication>
#include <QCursor>
#include <QDesktopServices> #include <QDesktopServices>
#include <QDir> #include <QDir>
#include <QFontDatabase> #include <QFontDatabase>
@ -278,12 +277,12 @@ void TrayWidget::showNotifications()
dismissNotifications(); dismissNotifications();
} }
void TrayWidget::showAtCursor() void TrayWidget::showUsingPositioningSettings()
{ {
if (m_menu) { if (m_menu) {
m_menu->showAtCursor(); m_menu->showUsingPositioningSettings();
} else { } else {
move(QCursor::pos()); move(Settings::values().appearance.positioning.positionToUse());
show(); show();
} }
} }

View File

@ -60,7 +60,7 @@ public slots:
void showOwnDeviceId(); void showOwnDeviceId();
void showLog(); void showLog();
void showNotifications(); void showNotifications();
void showAtCursor(); void showUsingPositioningSettings();
#ifdef SYNCTHINGTRAY_UNIFY_TRAY_MENUS #ifdef SYNCTHINGTRAY_UNIFY_TRAY_MENUS
void showInternalErrorsButton(); void showInternalErrorsButton();
void showInternalErrorsDialog(); void showInternalErrorsDialog();

View File

@ -20,24 +20,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0">
<widget class="QLabel" name="guiElementsLabel">
<property name="text">
<string>Optional GUI elements</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="showTrafficCheckBox">
<property name="text">
<string>Traffic statistics</string>
</property>
</widget>
</item>
<item row="0" column="1"> <item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="menuSizeHorizontalLayout">
<item> <item>
<widget class="QSpinBox" name="widthSpinBox"> <widget class="QSpinBox" name="widthSpinBox">
<property name="suffix">
<string> px</string>
</property>
<property name="minimum"> <property name="minimum">
<number>150</number> <number>150</number>
</property> </property>
@ -67,6 +56,9 @@
</item> </item>
<item> <item>
<widget class="QSpinBox" name="heightSpinBox"> <widget class="QSpinBox" name="heightSpinBox">
<property name="suffix">
<string> px</string>
</property>
<property name="minimum"> <property name="minimum">
<number>150</number> <number>150</number>
</property> </property>
@ -79,17 +71,37 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QLabel" name="sizePxLabel"> <spacer name="sizeHorizontalSpacer">
<property name="text"> <property name="orientation">
<string> px</string> <enum>Qt::Horizontal</enum>
</property> </property>
<property name="textFormat"> <property name="sizeType">
<enum>Qt::PlainText</enum> <enum>QSizePolicy::Preferred</enum>
</property> </property>
</widget> <property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item> </item>
</layout> </layout>
</item> </item>
<item row="2" column="0">
<widget class="QLabel" name="guiElementsLabel">
<property name="text">
<string>Optional GUI elements</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="showTrafficCheckBox">
<property name="text">
<string>Traffic statistics</string>
</property>
</widget>
</item>
<item row="3" column="0"> <item row="3" column="0">
<widget class="QLabel" name="frameShapeLabel"> <widget class="QLabel" name="frameShapeLabel">
<property name="text"> <property name="text">
@ -97,13 +109,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0">
<widget class="QLabel" name="frameShadowLabel">
<property name="text">
<string>Frame shadow</string>
</property>
</widget>
</item>
<item row="3" column="1"> <item row="3" column="1">
<widget class="QComboBox" name="frameShapeComboBox"> <widget class="QComboBox" name="frameShapeComboBox">
<item> <item>
@ -128,6 +133,13 @@
</item> </item>
</widget> </widget>
</item> </item>
<item row="4" column="0">
<widget class="QLabel" name="frameShadowLabel">
<property name="text">
<string>Frame shadow</string>
</property>
</widget>
</item>
<item row="4" column="1"> <item row="4" column="1">
<widget class="QComboBox" name="frameShadowComboBox"> <widget class="QComboBox" name="frameShadowComboBox">
<item> <item>
@ -147,14 +159,14 @@
</item> </item>
</widget> </widget>
</item> </item>
<item row="5" column="0"> <item row="6" column="0">
<widget class="QLabel" name="tabPosLabel"> <widget class="QLabel" name="tabPosLabel">
<property name="text"> <property name="text">
<string>Tab position</string> <string>Tab position</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="1"> <item row="6" column="1">
<widget class="QComboBox" name="tabPosComboBox"> <widget class="QComboBox" name="tabPosComboBox">
<item> <item>
<property name="text"> <property name="text">
@ -178,20 +190,129 @@
</item> </item>
</widget> </widget>
</item> </item>
<item row="6" column="0"> <item row="7" column="0">
<widget class="QLabel" name="colorsLabel"> <widget class="QLabel" name="colorsLabel">
<property name="text"> <property name="text">
<string>Colors</string> <string>Colors</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="1"> <item row="7" column="1">
<widget class="QCheckBox" name="brightTextColorsCheckBox"> <widget class="QCheckBox" name="brightTextColorsCheckBox">
<property name="text"> <property name="text">
<string>Bright custom text colors (use for dark color scheme)</string> <string>Bright custom text colors (use for dark color scheme)</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="8" column="0">
<widget class="QLabel" name="positioningLabel">
<property name="text">
<string>Positioning</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="8" column="1">
<layout class="QVBoxLayout" name="positioningVerticalLayout">
<item>
<widget class="QCheckBox" name="useCursorPosCheckBox">
<property name="text">
<string>Use cursor position</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="assumeIconPosLabel">
<property name="text">
<string>Otherwise assume tray icon coordinates to be:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="positioningHorizontalLayout">
<property name="spacing">
<number>10</number>
</property>
<item>
<widget class="QLabel" name="xPosLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>x:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="xPosSpinBox">
<property name="suffix">
<string> px</string>
</property>
<property name="minimum">
<number>-10000</number>
</property>
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="yPosLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>y:</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="yPosSpinBox">
<property name="suffix">
<string> px</string>
</property>
<property name="minimum">
<number>-10000</number>
</property>
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
<item>
<spacer name="positioningHorizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>

View File

@ -19,6 +19,7 @@
#endif #endif
#include <QApplication> #include <QApplication>
#include <QCursor>
#include <QFile> #include <QFile>
#include <QMessageBox> #include <QMessageBox>
#include <QSettings> #include <QSettings>
@ -51,6 +52,14 @@ namespace Settings {
*/ */
constexpr auto minActiveTimeInSeconds = 5; constexpr auto minActiveTimeInSeconds = 5;
/*!
* \brief Returns the position to use.
*/
QPoint Appearance::Positioning::positionToUse() const
{
return useCursorPosition ? QCursor::pos() : assumedIconPosition;
}
/*! /*!
* \brief Contains the processes for launching extra tools. * \brief Contains the processes for launching extra tools.
* \remarks Using std::unordered_map instead of QHash because SyncthingProcess can not be copied. * \remarks Using std::unordered_map instead of QHash because SyncthingProcess can not be copied.
@ -245,6 +254,11 @@ void restore()
appearance.tabPosition = settings.value(QStringLiteral("tabPos"), appearance.tabPosition).toInt(); appearance.tabPosition = settings.value(QStringLiteral("tabPos"), appearance.tabPosition).toInt();
appearance.brightTextColors = settings.value(QStringLiteral("brightTextColors"), appearance.brightTextColors).toBool(); appearance.brightTextColors = settings.value(QStringLiteral("brightTextColors"), appearance.brightTextColors).toBool();
v.statusIcons = StatusIconSettings(settings.value(QStringLiteral("statusIcons")).toString()); v.statusIcons = StatusIconSettings(settings.value(QStringLiteral("statusIcons")).toString());
settings.beginGroup(QStringLiteral("positioning"));
auto &positioning = appearance.positioning;
positioning.useCursorPosition = settings.value(QStringLiteral("useCursorPos"), positioning.useCursorPosition).toBool();
positioning.assumedIconPosition = settings.value(QStringLiteral("assumedIconPos"), positioning.assumedIconPosition).toPoint();
settings.endGroup();
settings.endGroup(); settings.endGroup();
settings.beginGroup(QStringLiteral("startup")); settings.beginGroup(QStringLiteral("startup"));
@ -336,6 +350,10 @@ void save()
settings.setValue(QStringLiteral("tabPos"), appearance.tabPosition); settings.setValue(QStringLiteral("tabPos"), appearance.tabPosition);
settings.setValue(QStringLiteral("brightTextColors"), appearance.brightTextColors); settings.setValue(QStringLiteral("brightTextColors"), appearance.brightTextColors);
settings.setValue(QStringLiteral("statusIcons"), v.statusIcons.toString()); settings.setValue(QStringLiteral("statusIcons"), v.statusIcons.toString());
settings.beginGroup(QStringLiteral("positioning"));
settings.setValue(QStringLiteral("useCursorPos"), appearance.positioning.useCursorPosition);
settings.setValue(QStringLiteral("assumedIconPos"), appearance.positioning.assumedIconPosition);
settings.endGroup();
settings.endGroup(); settings.endGroup();
settings.beginGroup(QStringLiteral("startup")); settings.beginGroup(QStringLiteral("startup"));
@ -469,6 +487,7 @@ Systemd::ServiceStatus Systemd::status(SyncthingConnection &connection) const
const auto isRelevant = service->isSystemdAvailable() && connection.isLocal(); const auto isRelevant = service->isSystemdAvailable() && connection.isLocal();
return ServiceStatus{ isRelevant, service->isRunning(), considerForReconnect && isRelevant, showButton && isRelevant }; return ServiceStatus{ isRelevant, service->isRunning(), considerForReconnect && isRelevant, showButton && isRelevant };
} }
#endif #endif
} // namespace Settings } // namespace Settings

View File

@ -10,6 +10,7 @@
#include <QByteArray> #include <QByteArray>
#include <QFrame> #include <QFrame>
#include <QHash> #include <QHash>
#include <QPoint>
#include <QSize> #include <QSize>
#include <QString> #include <QString>
#include <QTabWidget> #include <QTabWidget>
@ -51,6 +52,11 @@ struct SYNCTHINGWIDGETS_EXPORT Appearance {
int frameStyle = QFrame::NoFrame | QFrame::Plain; int frameStyle = QFrame::NoFrame | QFrame::Plain;
int tabPosition = QTabWidget::South; int tabPosition = QTabWidget::South;
bool brightTextColors = false; bool brightTextColors = false;
struct Positioning {
QPoint assumedIconPosition;
bool useCursorPosition = true;
QPoint positionToUse() const;
} positioning;
}; };
struct SYNCTHINGWIDGETS_EXPORT ToolParameter { struct SYNCTHINGWIDGETS_EXPORT ToolParameter {

View File

@ -457,6 +457,9 @@ bool AppearanceOptionPage::apply()
settings.tabPosition = ui()->tabPosComboBox->currentIndex(); settings.tabPosition = ui()->tabPosComboBox->currentIndex();
settings.brightTextColors = ui()->brightTextColorsCheckBox->isChecked(); settings.brightTextColors = ui()->brightTextColorsCheckBox->isChecked();
settings.positioning.useCursorPosition = ui()->useCursorPosCheckBox->isChecked();
settings.positioning.assumedIconPosition = QPoint(ui()->xPosSpinBox->value(), ui()->yPosSpinBox->value());
return true; return true;
} }
@ -493,7 +496,12 @@ void AppearanceOptionPage::reset()
} }
ui()->frameShadowComboBox->setCurrentIndex(index); ui()->frameShadowComboBox->setCurrentIndex(index);
ui()->tabPosComboBox->setCurrentIndex(settings.tabPosition); ui()->tabPosComboBox->setCurrentIndex(settings.tabPosition);
ui()->brightTextColorsCheckBox->setChecked(settings.brightTextColors); ui()->brightTextColorsCheckBox->setChecked(settings.brightTextColors);
ui()->useCursorPosCheckBox->setChecked(settings.positioning.useCursorPosition);
ui()->xPosSpinBox->setValue(settings.positioning.assumedIconPosition.x());
ui()->yPosSpinBox->setValue(settings.positioning.assumedIconPosition.y());
} }
// IconsOptionPage // IconsOptionPage