Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow any window to be bounds-checked #4802

Merged
merged 4 commits into from
Sep 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/controllers/commands/CommandController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 "";
});
Expand Down
82 changes: 82 additions & 0 deletions src/util/WidgetHelpers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#include "util/WidgetHelpers.hpp"

#include <QCursor>
#include <QGuiApplication>
#include <QPoint>
#include <QScreen>
#include <QWidget>

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
29 changes: 29 additions & 0 deletions src/util/WidgetHelpers.hpp
Original file line number Diff line number Diff line change
@@ -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
68 changes: 2 additions & 66 deletions src/widgets/BaseWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 *)
Expand Down Expand Up @@ -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)
Expand Down
19 changes: 2 additions & 17 deletions src/widgets/BaseWindow.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "common/FlagsEnum.hpp"
#include "util/WidgetHelpers.hpp"
#include "widgets/BaseWidget.hpp"

#include <pajlada/signals/signalholder.hpp>
Expand Down Expand Up @@ -36,17 +37,6 @@ 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> flags_ = None,
Expand All @@ -65,7 +55,7 @@ class BaseWindow : public BaseWidget
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;
Expand Down Expand Up @@ -110,11 +100,6 @@ class BaseWindow : public BaseWidget
private:
void init();

/**
*
**/
void moveWithinScreen(QPoint point, QPoint origin);

void calcButtonsSizes();
void drawCustomWindowFrame(QPainter &painter);
void onFocusLost();
Expand Down
2 changes: 1 addition & 1 deletion src/widgets/dialogs/EmotePopup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions src/widgets/helper/ChannelView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down Expand Up @@ -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();
}

Expand Down
2 changes: 1 addition & 1 deletion src/widgets/splits/SplitHeader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down