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
This commit is contained in:
pajlada 2023-08-05 13:22:37 +02:00 committed by GitHub
parent b98be3b0f3
commit 9e2eb0dd29
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 63 additions and 54 deletions

View file

@ -28,6 +28,7 @@
- Bugfix: Fix visual glitches with smooth scrolling. (#4501) - Bugfix: Fix visual glitches with smooth scrolling. (#4501)
- Bugfix: Fixed pings firing for the "Your username" highlight when not signed in. (#4698) - 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 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 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 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) - Bugfix: Fixed highlights sometimes not working after changing sound device, or switching users in your operating system. (#4729)

View file

@ -923,7 +923,8 @@ void CommandController::initialize(Settings &, Paths &paths)
static_cast<QWidget *>(&(getApp()->windows->getMainWindow())), static_cast<QWidget *>(&(getApp()->windows->getMainWindow())),
currentSplit); currentSplit);
userPopup->setData(userName, channel); userPopup->setData(userName, channel);
userPopup->move(QCursor::pos()); userPopup->moveTo(QCursor::pos(), false,
BaseWindow::BoundsChecker::CursorPosition);
userPopup->show(); userPopup->show();
return ""; return "";
}); });

View file

@ -10,6 +10,7 @@
#include "widgets/helper/EffectLabel.hpp" #include "widgets/helper/EffectLabel.hpp"
#include "widgets/Label.hpp" #include "widgets/Label.hpp"
#include "widgets/TooltipWidget.hpp" #include "widgets/TooltipWidget.hpp"
#include "widgets/Window.hpp"
#include <QApplication> #include <QApplication>
#include <QFont> #include <QFont>
@ -240,18 +241,6 @@ void BaseWindow::init()
#endif #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) void BaseWindow::setActionOnFocusLoss(ActionOnFocusLoss value)
{ {
this->actionOnFocusLoss_ = value; this->actionOnFocusLoss_ = value;
@ -514,7 +503,7 @@ void BaseWindow::leaveEvent(QEvent *)
TooltipWidget::instance()->hide(); TooltipWidget::instance()->hide();
} }
void BaseWindow::moveTo(QWidget *parent, QPoint point, bool offset) void BaseWindow::moveTo(QPoint point, bool offset, BoundsChecker boundsChecker)
{ {
if (offset) if (offset)
{ {
@ -522,7 +511,26 @@ void BaseWindow::moveTo(QWidget *parent, QPoint point, bool offset)
point.ry() += 16; 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 *) void BaseWindow::resizeEvent(QResizeEvent *)
@ -576,24 +584,13 @@ void BaseWindow::closeEvent(QCloseEvent *)
void BaseWindow::showEvent(QShowEvent *) 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 // 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) if (screen == nullptr)
{ {
screen = QApplication::primaryScreen(); screen = QApplication::primaryScreen();
@ -603,6 +600,9 @@ void BaseWindow::moveIntoDesktopRect(QPoint point)
bool stickRight = false; bool stickRight = false;
bool stickBottom = false; bool stickBottom = false;
const auto w = this->frameGeometry().width();
const auto h = this->frameGeometry().height();
if (point.x() < bounds.left()) if (point.x() < bounds.left())
{ {
point.setX(bounds.left()); point.setX(bounds.left());
@ -611,15 +611,15 @@ void BaseWindow::moveIntoDesktopRect(QPoint point)
{ {
point.setY(bounds.top()); point.setY(bounds.top());
} }
if (point.x() + this->width() > bounds.right()) if (point.x() + w > bounds.right())
{ {
stickRight = true; 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; stickBottom = true;
point.setY(bounds.bottom() - this->height()); point.setY(bounds.bottom() - h);
} }
if (stickRight && stickBottom) if (stickRight && stickBottom)

View file

@ -36,6 +36,17 @@ public:
DisableLayoutSave = 128, 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 }; enum ActionOnFocusLoss { Nothing, Delete, Close, Hide };
explicit BaseWindow(FlagsEnum<Flags> flags_ = None, explicit BaseWindow(FlagsEnum<Flags> flags_ = None,
@ -51,15 +62,12 @@ public:
std::function<void()> onClicked); std::function<void()> onClicked);
EffectLabel *addTitleBarLabel(std::function<void()> onClicked); EffectLabel *addTitleBarLabel(std::function<void()> onClicked);
void setStayInScreenRect(bool value);
bool getStayInScreenRect() const;
void setActionOnFocusLoss(ActionOnFocusLoss value); void setActionOnFocusLoss(ActionOnFocusLoss value);
ActionOnFocusLoss getActionOnFocusLoss() const; 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; float qtFontScale() const;
pajlada::Signals::NoArgSignal closing; pajlada::Signals::NoArgSignal closing;
@ -101,7 +109,12 @@ protected:
private: private:
void init(); void init();
void moveIntoDesktopRect(QPoint point);
/**
*
**/
void moveWithinScreen(QPoint point, QPoint origin);
void calcButtonsSizes(); void calcButtonsSizes();
void drawCustomWindowFrame(QPainter &painter); void drawCustomWindowFrame(QPainter &painter);
void onFocusLost(); void onFocusLost();
@ -121,7 +134,6 @@ private:
bool enableCustomFrame_; bool enableCustomFrame_;
ActionOnFocusLoss actionOnFocusLoss_ = Nothing; ActionOnFocusLoss actionOnFocusLoss_ = Nothing;
bool frameless_; bool frameless_;
bool stayInScreenRect_ = false;
bool shown_ = false; bool shown_ = false;
FlagsEnum<Flags> flags_; FlagsEnum<Flags> flags_;
float nativeScale_ = 1; float nativeScale_ = 1;

View file

@ -27,8 +27,6 @@ TooltipWidget::TooltipWidget(BaseWidget *parent)
this->setAttribute(Qt::WA_TranslucentBackground); this->setAttribute(Qt::WA_TranslucentBackground);
this->setWindowFlag(Qt::WindowStaysOnTopHint, true); this->setWindowFlag(Qt::WindowStaysOnTopHint, true);
this->setStayInScreenRect(true);
// Default to using vertical layout // Default to using vertical layout
this->initializeVLayout(); this->initializeVLayout();
this->setLayout(this->vLayout_); this->setLayout(this->vLayout_);

View file

@ -206,8 +206,9 @@ EmotePopup::EmotePopup(QWidget *parent)
, search_(new QLineEdit()) , search_(new QLineEdit())
, notebook_(new Notebook(this)) , notebook_(new Notebook(this))
{ {
this->setStayInScreenRect(true); // this->setStayInScreenRect(true);
this->moveTo(this, getApp()->windows->emotePopupPos(), false); this->moveTo(getApp()->windows->emotePopupPos(), false,
BaseWindow::BoundsChecker::DesiredPosition);
auto *layout = new QVBoxLayout(); auto *layout = new QVBoxLayout();
this->getLayoutContainer()->setLayout(layout); this->getLayoutContainer()->setLayout(layout);

View file

@ -29,7 +29,6 @@ ReplyThreadPopup::ReplyThreadPopup(bool closeAutomatically, QWidget *parent,
, split_(split) , split_(split)
{ {
this->setWindowTitle(QStringLiteral("Reply Thread")); this->setWindowTitle(QStringLiteral("Reply Thread"));
this->setStayInScreenRect(true);
HotkeyController::HotkeyMap actions{ HotkeyController::HotkeyMap actions{
{"delete", {"delete",

View file

@ -142,7 +142,6 @@ UserInfoPopup::UserInfoPopup(bool closeAutomatically, QWidget *parent,
assert(split != nullptr && assert(split != nullptr &&
"split being nullptr causes lots of bugs down the road"); "split being nullptr causes lots of bugs down the road");
this->setWindowTitle("Usercard"); this->setWindowTitle("Usercard");
this->setStayInScreenRect(true);
HotkeyController::HotkeyMap actions{ HotkeyController::HotkeyMap actions{
{"delete", {"delete",
@ -715,9 +714,6 @@ void UserInfoPopup::setData(const QString &name,
this->userStateChanged_.invoke(); this->userStateChanged_.invoke();
this->updateLatestMessages(); this->updateLatestMessages();
QTimer::singleShot(1, this, [this] {
this->setStayInScreenRect(true);
});
} }
void UserInfoPopup::updateLatestMessages() void UserInfoPopup::updateLatestMessages()

View file

@ -46,7 +46,6 @@ QuickSwitcherPopup::QuickSwitcherPopup(QWidget *parent)
this->initWidgets(); this->initWidgets();
this->setStayInScreenRect(true);
const QRect geom = parent->geometry(); const QRect geom = parent->geometry();
// This places the popup in the middle of the parent widget // This places the popup in the middle of the parent widget
this->setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, this->setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter,

View file

@ -1810,7 +1810,8 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event)
} }
} }
tooltipWidget->moveTo(this, event->globalPos()); tooltipWidget->moveTo(event->globalPos(), true,
BaseWindow::BoundsChecker::CursorPosition);
tooltipWidget->setWordWrap(isLinkValid); tooltipWidget->setWordWrap(isLinkValid);
tooltipWidget->show(); tooltipWidget->show();
} }
@ -2663,8 +2664,9 @@ void ChannelView::showUserInfoPopup(const QString &userName,
: this->underlyingChannel_; : this->underlyingChannel_;
userPopup->setData(userName, contextChannel, openingChannel); userPopup->setData(userName, contextChannel, openingChannel);
QPoint offset(int(150 * this->scale()), int(70 * this->scale())); QPoint offset(userPopup->width() / 3, userPopup->height() / 5);
userPopup->move(QCursor::pos() - offset); userPopup->moveTo(QCursor::pos() - offset, false,
BaseWindow::BoundsChecker::CursorPosition);
userPopup->show(); userPopup->show();
} }

View file

@ -964,7 +964,7 @@ void SplitHeader::enterEvent(QEvent *event)
auto pos = this->mapToGlobal(this->rect().bottomLeft()) + auto pos = this->mapToGlobal(this->rect().bottomLeft()) +
QPoint((this->width() - tooltip->width()) / 2, 1); QPoint((this->width() - tooltip->width()) / 2, 1);
tooltip->moveTo(this, pos, false); tooltip->moveTo(pos, false, BaseWindow::BoundsChecker::CursorPosition);
tooltip->show(); tooltip->show();
} }