From d5188b88145b7e90df37911017110b5676a8c05c Mon Sep 17 00:00:00 2001 From: pajlada Date: Sat, 5 Aug 2023 13:22:37 +0200 Subject: [PATCH] Fix tooltip & popup positioning (#4740) * Fix tooltip & popup positioning This tries to ensure the tooltip & popups are created on the correct monitor * Add changelog entry * Clean up debug output * Use the full frame geometry to figure out screen bound movements * Remove the now-unused `setStayInScreenRect` function * Change the UserInfoPopup offset to be based on its width & height instead * Remove more debug output --- CHANGELOG.md | 1 + .../commands/CommandController.cpp | 3 +- src/widgets/BaseWindow.cpp | 64 +++++++++---------- src/widgets/BaseWindow.hpp | 26 ++++++-- src/widgets/TooltipWidget.cpp | 2 - src/widgets/dialogs/EmotePopup.cpp | 5 +- src/widgets/dialogs/ReplyThreadPopup.cpp | 1 - src/widgets/dialogs/UserInfoPopup.cpp | 4 -- .../dialogs/switcher/QuickSwitcherPopup.cpp | 1 - src/widgets/helper/ChannelView.cpp | 8 ++- src/widgets/splits/SplitHeader.cpp | 2 +- 11 files changed, 63 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc7fadb589a..47422626dc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ - Bugfix: Fix visual glitches with smooth scrolling. (#4501) - Bugfix: Fixed pings firing for the "Your username" highlight when not signed in. (#4698) - Bugfix: Fixed partially broken filters on Qt 6 builds. (#4702) +- Bugfix: Fixed tooltips & popups sometimes showing up on the wrong monitor. (#4740) - Bugfix: Fixed some network errors having `0` as their HTTP status. (#4704) - Bugfix: Fixed crash that could occurr when closing the usercard too quickly after blocking or unblocking a user. (#4711) - Bugfix: Fixed highlights sometimes not working after changing sound device, or switching users in your operating system. (#4729) diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 62da0b83b1e..fa1255a9a50 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -923,7 +923,8 @@ void CommandController::initialize(Settings &, Paths &paths) static_cast(&(getApp()->windows->getMainWindow())), currentSplit); userPopup->setData(userName, channel); - userPopup->move(QCursor::pos()); + userPopup->moveTo(QCursor::pos(), false, + BaseWindow::BoundsChecker::CursorPosition); userPopup->show(); return ""; }); diff --git a/src/widgets/BaseWindow.cpp b/src/widgets/BaseWindow.cpp index 60365081e67..f02cdf472ec 100644 --- a/src/widgets/BaseWindow.cpp +++ b/src/widgets/BaseWindow.cpp @@ -10,6 +10,7 @@ #include "widgets/helper/EffectLabel.hpp" #include "widgets/Label.hpp" #include "widgets/TooltipWidget.hpp" +#include "widgets/Window.hpp" #include #include @@ -240,18 +241,6 @@ void BaseWindow::init() #endif } -void BaseWindow::setStayInScreenRect(bool value) -{ - this->stayInScreenRect_ = value; - - this->moveIntoDesktopRect(this->pos()); -} - -bool BaseWindow::getStayInScreenRect() const -{ - return this->stayInScreenRect_; -} - void BaseWindow::setActionOnFocusLoss(ActionOnFocusLoss value) { this->actionOnFocusLoss_ = value; @@ -514,7 +503,7 @@ void BaseWindow::leaveEvent(QEvent *) TooltipWidget::instance()->hide(); } -void BaseWindow::moveTo(QWidget *parent, QPoint point, bool offset) +void BaseWindow::moveTo(QPoint point, bool offset, BoundsChecker boundsChecker) { if (offset) { @@ -522,7 +511,26 @@ void BaseWindow::moveTo(QWidget *parent, QPoint point, bool offset) point.ry() += 16; } - this->moveIntoDesktopRect(point); + switch (boundsChecker) + { + case BoundsChecker::Off: { + // The bounds checker is off, *just* move the window + this->move(point); + } + break; + + case BoundsChecker::CursorPosition: { + // The bounds checker is on, use the cursor position as the origin + this->moveWithinScreen(point, QCursor::pos()); + } + break; + + case BoundsChecker::DesiredPosition: { + // The bounds checker is on, use the desired position as the origin + this->moveWithinScreen(point, point); + } + break; + } } void BaseWindow::resizeEvent(QResizeEvent *) @@ -576,24 +584,13 @@ void BaseWindow::closeEvent(QCloseEvent *) void BaseWindow::showEvent(QShowEvent *) { - this->moveIntoDesktopRect(this->pos()); - if (this->frameless_) - { - QTimer::singleShot(30, this, [this] { - this->moveIntoDesktopRect(this->pos()); - }); - } } -void BaseWindow::moveIntoDesktopRect(QPoint point) +void BaseWindow::moveWithinScreen(QPoint point, QPoint origin) { - if (!this->stayInScreenRect_) - { - return; - } - // move the widget into the screen geometry if it's not already in there - auto *screen = QApplication::screenAt(point); + auto *screen = QApplication::screenAt(origin); + if (screen == nullptr) { screen = QApplication::primaryScreen(); @@ -603,6 +600,9 @@ void BaseWindow::moveIntoDesktopRect(QPoint point) bool stickRight = false; bool stickBottom = false; + const auto w = this->frameGeometry().width(); + const auto h = this->frameGeometry().height(); + if (point.x() < bounds.left()) { point.setX(bounds.left()); @@ -611,15 +611,15 @@ void BaseWindow::moveIntoDesktopRect(QPoint point) { point.setY(bounds.top()); } - if (point.x() + this->width() > bounds.right()) + if (point.x() + w > bounds.right()) { stickRight = true; - point.setX(bounds.right() - this->width()); + point.setX(bounds.right() - w); } - if (point.y() + this->height() > bounds.bottom()) + if (point.y() + h > bounds.bottom()) { stickBottom = true; - point.setY(bounds.bottom() - this->height()); + point.setY(bounds.bottom() - h); } if (stickRight && stickBottom) diff --git a/src/widgets/BaseWindow.hpp b/src/widgets/BaseWindow.hpp index 106cc1a3e5d..ec043f694b9 100644 --- a/src/widgets/BaseWindow.hpp +++ b/src/widgets/BaseWindow.hpp @@ -36,6 +36,17 @@ class BaseWindow : public BaseWidget DisableLayoutSave = 128, }; + enum class BoundsChecker { + // Don't attempt to do any "stay in screen" stuff, just move me! + Off, + + // Attempt to keep the window within bounds of the screen the cursor is on + CursorPosition, + + // Attempt to keep the window within bounds of the screen the desired position is on + DesiredPosition, + }; + enum ActionOnFocusLoss { Nothing, Delete, Close, Hide }; explicit BaseWindow(FlagsEnum flags_ = None, @@ -51,15 +62,12 @@ class BaseWindow : public BaseWidget std::function onClicked); EffectLabel *addTitleBarLabel(std::function onClicked); - void setStayInScreenRect(bool value); - bool getStayInScreenRect() const; - void setActionOnFocusLoss(ActionOnFocusLoss value); ActionOnFocusLoss getActionOnFocusLoss() const; - void moveTo(QWidget *widget, QPoint point, bool offset = true); + void moveTo(QPoint point, bool offset, BoundsChecker boundsChecker); - virtual float scale() const override; + float scale() const override; float qtFontScale() const; pajlada::Signals::NoArgSignal closing; @@ -101,7 +109,12 @@ class BaseWindow : public BaseWidget private: void init(); - void moveIntoDesktopRect(QPoint point); + + /** + * + **/ + void moveWithinScreen(QPoint point, QPoint origin); + void calcButtonsSizes(); void drawCustomWindowFrame(QPainter &painter); void onFocusLost(); @@ -121,7 +134,6 @@ class BaseWindow : public BaseWidget bool enableCustomFrame_; ActionOnFocusLoss actionOnFocusLoss_ = Nothing; bool frameless_; - bool stayInScreenRect_ = false; bool shown_ = false; FlagsEnum flags_; float nativeScale_ = 1; diff --git a/src/widgets/TooltipWidget.cpp b/src/widgets/TooltipWidget.cpp index 98ad4b971be..f979c03d28c 100644 --- a/src/widgets/TooltipWidget.cpp +++ b/src/widgets/TooltipWidget.cpp @@ -27,8 +27,6 @@ TooltipWidget::TooltipWidget(BaseWidget *parent) this->setAttribute(Qt::WA_TranslucentBackground); this->setWindowFlag(Qt::WindowStaysOnTopHint, true); - this->setStayInScreenRect(true); - // Default to using vertical layout this->initializeVLayout(); this->setLayout(this->vLayout_); diff --git a/src/widgets/dialogs/EmotePopup.cpp b/src/widgets/dialogs/EmotePopup.cpp index 4781489f292..e7c2b62cf09 100644 --- a/src/widgets/dialogs/EmotePopup.cpp +++ b/src/widgets/dialogs/EmotePopup.cpp @@ -207,8 +207,9 @@ EmotePopup::EmotePopup(QWidget *parent) , search_(new QLineEdit()) , notebook_(new Notebook(this)) { - this->setStayInScreenRect(true); - this->moveTo(this, getApp()->windows->emotePopupPos(), false); + // this->setStayInScreenRect(true); + this->moveTo(getApp()->windows->emotePopupPos(), false, + BaseWindow::BoundsChecker::DesiredPosition); auto *layout = new QVBoxLayout(); this->getLayoutContainer()->setLayout(layout); diff --git a/src/widgets/dialogs/ReplyThreadPopup.cpp b/src/widgets/dialogs/ReplyThreadPopup.cpp index b2abd5fd9d8..5d688d7f2ec 100644 --- a/src/widgets/dialogs/ReplyThreadPopup.cpp +++ b/src/widgets/dialogs/ReplyThreadPopup.cpp @@ -29,7 +29,6 @@ ReplyThreadPopup::ReplyThreadPopup(bool closeAutomatically, QWidget *parent, , split_(split) { this->setWindowTitle(QStringLiteral("Reply Thread")); - this->setStayInScreenRect(true); HotkeyController::HotkeyMap actions{ {"delete", diff --git a/src/widgets/dialogs/UserInfoPopup.cpp b/src/widgets/dialogs/UserInfoPopup.cpp index b37f8d54b31..c1af0d24f7c 100644 --- a/src/widgets/dialogs/UserInfoPopup.cpp +++ b/src/widgets/dialogs/UserInfoPopup.cpp @@ -155,7 +155,6 @@ UserInfoPopup::UserInfoPopup(bool closeAutomatically, QWidget *parent, assert(split != nullptr && "split being nullptr causes lots of bugs down the road"); this->setWindowTitle("Usercard"); - this->setStayInScreenRect(true); HotkeyController::HotkeyMap actions{ {"delete", @@ -734,9 +733,6 @@ void UserInfoPopup::setData(const QString &name, this->userStateChanged_.invoke(); this->updateLatestMessages(); - QTimer::singleShot(1, this, [this] { - this->setStayInScreenRect(true); - }); } void UserInfoPopup::updateLatestMessages() diff --git a/src/widgets/dialogs/switcher/QuickSwitcherPopup.cpp b/src/widgets/dialogs/switcher/QuickSwitcherPopup.cpp index dd33ad54a76..40792d3ff3e 100644 --- a/src/widgets/dialogs/switcher/QuickSwitcherPopup.cpp +++ b/src/widgets/dialogs/switcher/QuickSwitcherPopup.cpp @@ -46,7 +46,6 @@ QuickSwitcherPopup::QuickSwitcherPopup(QWidget *parent) this->initWidgets(); - this->setStayInScreenRect(true); const QRect geom = parent->geometry(); // This places the popup in the middle of the parent widget this->setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index 9ed6b7aceec..107cdab02c9 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -1811,7 +1811,8 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event) } } - tooltipWidget->moveTo(this, event->globalPos()); + tooltipWidget->moveTo(event->globalPos(), true, + BaseWindow::BoundsChecker::CursorPosition); tooltipWidget->setWordWrap(isLinkValid); tooltipWidget->show(); } @@ -2664,8 +2665,9 @@ void ChannelView::showUserInfoPopup(const QString &userName, : this->underlyingChannel_; userPopup->setData(userName, contextChannel, openingChannel); - QPoint offset(int(150 * this->scale()), int(70 * this->scale())); - userPopup->move(QCursor::pos() - offset); + QPoint offset(userPopup->width() / 3, userPopup->height() / 5); + userPopup->moveTo(QCursor::pos() - offset, false, + BaseWindow::BoundsChecker::CursorPosition); userPopup->show(); } diff --git a/src/widgets/splits/SplitHeader.cpp b/src/widgets/splits/SplitHeader.cpp index d8eb2829a8f..4bce72ba609 100644 --- a/src/widgets/splits/SplitHeader.cpp +++ b/src/widgets/splits/SplitHeader.cpp @@ -964,7 +964,7 @@ void SplitHeader::enterEvent(QEvent *event) auto pos = this->mapToGlobal(this->rect().bottomLeft()) + QPoint((this->width() - tooltip->width()) / 2, 1); - tooltip->moveTo(this, pos, false); + tooltip->moveTo(pos, false, BaseWindow::BoundsChecker::CursorPosition); tooltip->show(); }