diff --git a/CHANGELOG.md b/CHANGELOG.md index 718318df..6564d9a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -87,6 +87,7 @@ - Bugfix: Removed ability to reload emotes really fast (#3450) - Bugfix: Re-add date of build to the "About" page on nightly versions. (#3464) - Bugfix: Fixed crash that would occur if the user right-clicked AutoMod badge. (#3496) +- Bugfix: Fixed being unable to drag the user card window from certain spots. (#3508) - Dev: Batch checking live status for channels with live notifications that aren't connected. (#3442) - Dev: Add GitHub action to test builds without precompiled headers enabled. (#3327) - Dev: Renamed CMake's build option `USE_SYSTEM_QT5KEYCHAIN` to `USE_SYSTEM_QTKEYCHAIN`. (#3103) diff --git a/src/widgets/dialogs/UserInfoPopup.cpp b/src/widgets/dialogs/UserInfoPopup.cpp index 2ccd4f78..3be2951b 100644 --- a/src/widgets/dialogs/UserInfoPopup.cpp +++ b/src/widgets/dialogs/UserInfoPopup.cpp @@ -133,6 +133,7 @@ UserInfoPopup::UserInfoPopup(bool closeAutomatically, QWidget *parent) : userInfoPopupFlags, parent) , hack_(new bool) + , dragTimer_(this) { this->setWindowTitle("Usercard"); this->setStayInScreenRect(true); @@ -428,6 +429,21 @@ UserInfoPopup::UserInfoPopup(bool closeAutomatically, QWidget *parent) this->installEvents(); this->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Policy::Ignored); + + this->dragTimer_.callOnTimeout( + [this, hack = std::weak_ptr(this->hack_)] { + if (!hack.lock()) + { + // Ensure this timer is never called after the object has been destroyed + return; + } + if (!this->isMoving_) + { + return; + } + + this->move(this->requestedDragPos_); + }); } void UserInfoPopup::themeChangedEvent() @@ -453,6 +469,36 @@ void UserInfoPopup::scaleChangedEvent(float /*scale*/) }); } +void UserInfoPopup::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::MouseButton::LeftButton) + { + this->dragTimer_.start(std::chrono::milliseconds(17)); + this->startPosDrag_ = event->pos(); + this->movingRelativePos = event->localPos(); + } +} + +void UserInfoPopup::mouseReleaseEvent(QMouseEvent *event) +{ + this->dragTimer_.stop(); + this->isMoving_ = false; +} + +void UserInfoPopup::mouseMoveEvent(QMouseEvent *event) +{ + // Drag the window by the amount changed from inital position + // Note that we provide a few *units* of deadzone so people don't + // start dragging the window if they are slow at clicking. + auto movePos = event->pos() - this->startPosDrag_; + if (this->isMoving_ || movePos.manhattanLength() > 10.0) + { + this->requestedDragPos_ = + (event->screenPos() - this->movingRelativePos).toPoint(); + this->isMoving_ = true; + } +} + void UserInfoPopup::installEvents() { std::shared_ptr ignoreNext = std::make_shared(false); diff --git a/src/widgets/dialogs/UserInfoPopup.hpp b/src/widgets/dialogs/UserInfoPopup.hpp index dc45586d..6aff6e19 100644 --- a/src/widgets/dialogs/UserInfoPopup.hpp +++ b/src/widgets/dialogs/UserInfoPopup.hpp @@ -6,6 +6,8 @@ #include #include +#include + class QCheckBox; namespace chatterino { @@ -26,6 +28,9 @@ public: protected: virtual void themeChangedEvent() override; virtual void scaleChangedEvent(float scale) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; private: void installEvents(); @@ -41,6 +46,19 @@ private: QString avatarUrl_; ChannelPtr channel_; + // isMoving_ is set to true if the user is holding the left mouse button down and has moved the mouse a small amount away from the original click point (startPosDrag_) + bool isMoving_ = false; + + // startPosDrag_ is the coordinates where the user originally pressed the mouse button down to start dragging + QPoint startPosDrag_; + + // requestDragPos_ is the final screen coordinates where the widget should be moved to. + // Takes the relative position of where the user originally clicked the widget into account + QPoint requestedDragPos_; + + // dragTimer_ is called ~60 times per second once the user has initiated dragging + QTimer dragTimer_; + pajlada::Signals::NoArgSignal userStateChanged_; std::unique_ptr refreshConnection_;