diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c2909e142..a99a4a264 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -458,6 +458,8 @@ set(SOURCE_FILES util/Twitch.cpp util/Twitch.hpp util/TypeName.hpp + util/WidgetHelpers.cpp + util/WidgetHelpers.hpp util/WindowsHelper.cpp util/WindowsHelper.hpp util/XDGDesktopFile.cpp diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 51ac02fa4..894af5c03 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -926,7 +926,7 @@ void CommandController::initialize(Settings &, Paths &paths) currentSplit); userPopup->setData(userName, channel); userPopup->moveTo(QCursor::pos(), - BaseWindow::BoundsChecker::CursorPosition); + widgets::BoundsChecking::CursorPosition); userPopup->show(); return ""; }); diff --git a/src/util/WidgetHelpers.cpp b/src/util/WidgetHelpers.cpp new file mode 100644 index 000000000..7e6872760 --- /dev/null +++ b/src/util/WidgetHelpers.cpp @@ -0,0 +1,82 @@ +#include "util/WidgetHelpers.hpp" + +#include +#include +#include +#include +#include + +namespace { + +/// Move the `window` into the `screen` geometry if it's not already in there. +void moveWithinScreen(QWidget *window, QScreen *screen, QPoint point) +{ + if (screen == nullptr) + { + screen = QGuiApplication::primaryScreen(); + } + + const QRect bounds = screen->availableGeometry(); + + bool stickRight = false; + bool stickBottom = false; + + const auto w = window->frameGeometry().width(); + const auto h = window->frameGeometry().height(); + + if (point.x() < bounds.left()) + { + point.setX(bounds.left()); + } + if (point.y() < bounds.top()) + { + point.setY(bounds.top()); + } + if (point.x() + w > bounds.right()) + { + stickRight = true; + point.setX(bounds.right() - w); + } + if (point.y() + h > bounds.bottom()) + { + stickBottom = true; + point.setY(bounds.bottom() - h); + } + + if (stickRight && stickBottom) + { + const QPoint globalCursorPos = QCursor::pos(); + point.setY(globalCursorPos.y() - window->height() - 16); + } + + window->move(point); +} + +} // namespace + +namespace chatterino::widgets { + +void moveWindowTo(QWidget *window, QPoint position, BoundsChecking mode) +{ + switch (mode) + { + case BoundsChecking::Off: { + window->move(position); + } + break; + + case BoundsChecking::CursorPosition: { + moveWithinScreen(window, QGuiApplication::screenAt(QCursor::pos()), + position); + } + break; + + case BoundsChecking::DesiredPosition: { + moveWithinScreen(window, QGuiApplication::screenAt(position), + position); + } + break; + } +} + +} // namespace chatterino::widgets diff --git a/src/util/WidgetHelpers.hpp b/src/util/WidgetHelpers.hpp new file mode 100644 index 000000000..7f57ad393 --- /dev/null +++ b/src/util/WidgetHelpers.hpp @@ -0,0 +1,29 @@ +#pragma once + +class QWidget; +class QPoint; +class QScreen; + +namespace chatterino::widgets { + +enum class BoundsChecking { + /// Don't do any bounds checking (equivalent to `QWidget::move`). + 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, +}; + +/// Moves the `window` to the (global) `position` +/// while doing bounds-checking according to `mode` to ensure the window stays on one screen. +/// +/// @param window The window to move. +/// @param position The global position to move the window to. +/// @param mode The desired bounds checking. +void moveWindowTo(QWidget *window, QPoint position, + BoundsChecking mode = BoundsChecking::DesiredPosition); + +} // namespace chatterino::widgets diff --git a/src/widgets/BaseWindow.cpp b/src/widgets/BaseWindow.cpp index dd54f67ae..7fc1a3b53 100644 --- a/src/widgets/BaseWindow.cpp +++ b/src/widgets/BaseWindow.cpp @@ -503,28 +503,9 @@ void BaseWindow::leaveEvent(QEvent *) TooltipWidget::instance()->hide(); } -void BaseWindow::moveTo(QPoint point, BoundsChecker boundsChecker) +void BaseWindow::moveTo(QPoint point, widgets::BoundsChecking mode) { - 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; - } + widgets::moveWindowTo(this, point, mode); } void BaseWindow::resizeEvent(QResizeEvent *) @@ -580,51 +561,6 @@ void BaseWindow::showEvent(QShowEvent *) { } -void BaseWindow::moveWithinScreen(QPoint point, QPoint origin) -{ - // move the widget into the screen geometry if it's not already in there - auto *screen = QApplication::screenAt(origin); - - if (screen == nullptr) - { - screen = QApplication::primaryScreen(); - } - const QRect bounds = screen->availableGeometry(); - - 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()); - } - if (point.y() < bounds.top()) - { - point.setY(bounds.top()); - } - if (point.x() + w > bounds.right()) - { - stickRight = true; - point.setX(bounds.right() - w); - } - if (point.y() + h > bounds.bottom()) - { - stickBottom = true; - point.setY(bounds.bottom() - h); - } - - if (stickRight && stickBottom) - { - const QPoint globalCursorPos = QCursor::pos(); - point.setY(globalCursorPos.y() - this->height() - 16); - } - - this->move(point); -} - #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message, qintptr *result) diff --git a/src/widgets/BaseWindow.hpp b/src/widgets/BaseWindow.hpp index 076b3ab76..864e8f9bf 100644 --- a/src/widgets/BaseWindow.hpp +++ b/src/widgets/BaseWindow.hpp @@ -1,6 +1,7 @@ #pragma once #include "common/FlagsEnum.hpp" +#include "util/WidgetHelpers.hpp" #include "widgets/BaseWidget.hpp" #include @@ -36,17 +37,6 @@ public: 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, @@ -65,7 +55,7 @@ public: void setActionOnFocusLoss(ActionOnFocusLoss value); ActionOnFocusLoss getActionOnFocusLoss() const; - void moveTo(QPoint point, BoundsChecker boundsChecker); + void moveTo(QPoint point, widgets::BoundsChecking mode); float scale() const override; float qtFontScale() const; @@ -110,11 +100,6 @@ protected: private: void init(); - /** - * - **/ - void moveWithinScreen(QPoint point, QPoint origin); - void calcButtonsSizes(); void drawCustomWindowFrame(QPainter &painter); void onFocusLost(); diff --git a/src/widgets/dialogs/EmotePopup.cpp b/src/widgets/dialogs/EmotePopup.cpp index 66d9f2df2..985562409 100644 --- a/src/widgets/dialogs/EmotePopup.cpp +++ b/src/widgets/dialogs/EmotePopup.cpp @@ -207,7 +207,7 @@ EmotePopup::EmotePopup(QWidget *parent) { // this->setStayInScreenRect(true); this->moveTo(getApp()->windows->emotePopupPos(), - BaseWindow::BoundsChecker::DesiredPosition); + widgets::BoundsChecking::DesiredPosition); auto *layout = new QVBoxLayout(); this->getLayoutContainer()->setLayout(layout); diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index a2beffb02..3d1924e86 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -1832,7 +1832,7 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event) } tooltipWidget->moveTo(event->globalPos() + QPoint(16, 16), - BaseWindow::BoundsChecker::CursorPosition); + widgets::BoundsChecking::CursorPosition); tooltipWidget->setWordWrap(isLinkValid); tooltipWidget->show(); } @@ -2687,7 +2687,7 @@ void ChannelView::showUserInfoPopup(const QString &userName, QPoint offset(userPopup->width() / 3, userPopup->height() / 5); userPopup->moveTo(QCursor::pos() - offset, - BaseWindow::BoundsChecker::CursorPosition); + widgets::BoundsChecking::CursorPosition); userPopup->show(); } diff --git a/src/widgets/splits/SplitHeader.cpp b/src/widgets/splits/SplitHeader.cpp index 4991f7a95..8e5e3a034 100644 --- a/src/widgets/splits/SplitHeader.cpp +++ b/src/widgets/splits/SplitHeader.cpp @@ -956,7 +956,7 @@ void SplitHeader::enterEvent(QEvent *event) auto pos = this->mapToGlobal(this->rect().bottomLeft()) + QPoint((this->width() - tooltip->width()) / 2, 1); - tooltip->moveTo(pos, BaseWindow::BoundsChecker::CursorPosition); + tooltip->moveTo(pos, widgets::BoundsChecking::CursorPosition); tooltip->show(); }