syncthingtray/tray/gui/traymenu.cpp
Martchus a9cc948099 Allow resizing/moving tray widget
This might be useful on Wayland where positioning the popup is otherwise
not possible at all. Combined with a session restoration mechanism this
might actually be a feasible workaround. The user just needed to position
the popup once wherever it is wanted to show up and then the compositor
could remember that position.

Especially the resizing would also be useful on other platforms (and is by
the way also already possible when using the Plasmoid showing it as part of
the system tray Plasmoid).
2023-10-06 17:05:40 +02:00

169 lines
4.6 KiB
C++

#include "./traymenu.h"
#include "./trayicon.h"
#include "./traywidget.h"
#include <syncthingwidgets/settings/settings.h>
#include <qtutilities/misc/dialogutils.h>
#include <QApplication>
#include <QHBoxLayout>
#include <QPaintEvent>
#include <QPainter>
#include <QWindow>
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
#define QT_SUPPORTS_SYSTEM_WINDOW_COMMANDS
#endif
using namespace QtUtilities;
namespace QtGui {
static constexpr auto border = 10;
TrayMenu::TrayMenu(TrayIcon *trayIcon, QWidget *parent)
: QMenu(parent)
, m_trayIcon(trayIcon)
, m_windowType(WindowType::Popup)
, m_startedSystemWindowCommand(false)
{
setObjectName(QStringLiteral("QtGui::TrayMenu"));
auto *const menuLayout = new QHBoxLayout;
menuLayout->setContentsMargins(0, 0, 0, 0);
menuLayout->setSpacing(0);
menuLayout->addWidget(m_trayWidget = new TrayWidget(this));
setLayout(menuLayout);
setPlatformMenu(nullptr);
setWindowFlags(Qt::FramelessWindowHint | Qt::Popup);
setWindowIcon(m_trayWidget->windowIcon());
}
QSize TrayMenu::sizeHint() const
{
return Settings::values().appearance.trayMenuSize;
}
/*!
* \brief Moves the specified \a innerRect at the specified \a point into the specified \a outerRect
* by altering \a point.
*/
static void moveInside(QPoint &point, const QSize &innerRect, const QRect &outerRect)
{
if (point.y() < outerRect.top()) {
point.setY(outerRect.top());
} else if (point.y() + innerRect.height() > outerRect.bottom()) {
point.setY(outerRect.bottom() - innerRect.height());
}
if (point.x() < outerRect.left()) {
point.setX(outerRect.left());
} else if (point.x() + innerRect.width() > outerRect.right()) {
point.setX(outerRect.right() - innerRect.width());
}
}
void TrayMenu::showUsingPositioningSettings()
{
resize(sizeHint());
auto pos = Settings::values().appearance.positioning.positionToUse();
if (pos.has_value()) {
moveInside(pos.value(), size(), availableScreenGeometryAtPoint(pos.value()));
popup(pos.value());
} else {
show();
}
activateWindow();
}
void TrayMenu::setWindowType(int windowType)
{
if (windowType >= 0 && windowType <= 2) {
setWindowType(static_cast<WindowType>(windowType));
}
}
void TrayMenu::setWindowType(WindowType windowType)
{
if (m_windowType == windowType) {
return;
}
auto flags = Qt::WindowFlags();
switch (m_windowType = windowType) {
case WindowType::Popup:
flags = Qt::FramelessWindowHint | Qt::Popup;
break;
case WindowType::NormalWindow:
flags = Qt::Window;
break;
case WindowType::CustomWindow:
flags = Qt::Dialog | Qt::CustomizeWindowHint;
break;
}
setWindowFlags(flags);
}
void TrayMenu::mousePressEvent(QMouseEvent *event)
{
// skip any special behavior if the tray menu is shown as a regular window
if (m_windowType == TrayMenu::WindowType::NormalWindow) {
return;
}
// try starting a system window resize/move to allow resizing/moving the borderless window
#ifdef QT_SUPPORTS_SYSTEM_WINDOW_COMMANDS
if (auto *const window = this->windowHandle()) {
const auto pos = event->pos();
auto edges = Qt::Edges();
if (pos.x() < border)
edges |= Qt::LeftEdge;
if (pos.x() >= width() - border)
edges |= Qt::RightEdge;
if (pos.y() < border)
edges |= Qt::TopEdge;
if (pos.y() >= height() - border)
edges |= Qt::BottomEdge;
m_startedSystemWindowCommand = edges ? window->startSystemResize(edges) : window->startSystemMove();
}
#endif
// fallback to the default behavior for the current window type if system window resize/move is not possible
if (!m_startedSystemWindowCommand) {
QMenu::mousePressEvent(event);
}
}
void TrayMenu::mouseReleaseEvent(QMouseEvent *event)
{
// cover cases analogous to TrayMenu::mousePressEvent()
if (m_windowType == TrayMenu::WindowType::NormalWindow) {
return;
}
if (m_startedSystemWindowCommand) {
m_startedSystemWindowCommand = false;
} else {
QMenu::mouseReleaseEvent(event);
}
}
void TrayMenu::paintEvent(QPaintEvent *event)
{
if (m_windowType == WindowType::Popup) {
QMenu::paintEvent(event);
} else {
QPainter(this).fillRect(event->rect(), palette().window());
QWidget::paintEvent(event);
}
}
void TrayMenu::focusOutEvent(QFocusEvent *)
{
if (m_windowType == WindowType::CustomWindow) {
if (const auto *fw = focusWidget(); fw->hasFocus()) {
return;
}
close();
}
}
} // namespace QtGui