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).
169 lines
4.6 KiB
C++
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
|