From 65bfec963b54fd05f42b80810be1f7174233891b Mon Sep 17 00:00:00 2001 From: nerix Date: Sat, 1 Jun 2024 12:38:39 +0200 Subject: [PATCH] feat(emote-popup): save size of popup (#5415) * fix: remove added margins from emote window position * chore: add changelog entry * feat: store size of emote window * chore: update changelog entry * fix: disable layout save * fix: PCH moment * fix: multiply by scale --- CHANGELOG.md | 1 + src/common/WindowDescriptors.cpp | 12 ++++-- src/common/WindowDescriptors.hpp | 2 +- src/singletons/WindowManager.cpp | 38 ++++++++++++------- src/singletons/WindowManager.hpp | 6 +-- src/util/WidgetHelpers.cpp | 60 ++++++++++++++++++++++++------ src/util/WidgetHelpers.hpp | 9 +++++ src/widgets/BaseWindow.cpp | 9 +++-- src/widgets/BaseWindow.hpp | 6 +-- src/widgets/dialogs/EmotePopup.cpp | 32 +++++++++++++--- src/widgets/dialogs/EmotePopup.hpp | 6 +++ src/widgets/splits/SplitInput.cpp | 2 - 12 files changed, 138 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15e552c4..1d9c4511 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Minor: Add option to customise Moderation buttons with images. (#5369) - Minor: Colored usernames now update on the fly when changing the "Color @usernames" setting. (#5300) - Minor: Added `flags.action` filter variable, allowing you to filter on `/me` messages. (#5397) +- Minor: The size of the emote popup is now saved. (#5415) - Minor: Added the ability to duplicate tabs. (#5277) - Bugfix: Fixed tab move animation occasionally failing to start after closing a tab. (#5426) - Bugfix: If a network request errors with 200 OK, Qt's error code is now reported instead of the HTTP status. (#5378) diff --git a/src/common/WindowDescriptors.cpp b/src/common/WindowDescriptors.cpp index e08069b2..6c02d5c9 100644 --- a/src/common/WindowDescriptors.cpp +++ b/src/common/WindowDescriptors.cpp @@ -219,9 +219,15 @@ WindowLayout WindowLayout::loadFromFile(const QString &path) } // Load emote popup position - QJsonObject emote_popup_obj = windowObj.value("emotePopup").toObject(); - layout.emotePopupPos_ = QPoint(emote_popup_obj.value("x").toInt(), - emote_popup_obj.value("y").toInt()); + { + auto emotePopup = windowObj["emotePopup"].toObject(); + layout.emotePopupBounds_ = QRect{ + emotePopup["x"].toInt(), + emotePopup["y"].toInt(), + emotePopup["width"].toInt(), + emotePopup["height"].toInt(), + }; + } layout.windows_.emplace_back(std::move(window)); } diff --git a/src/common/WindowDescriptors.hpp b/src/common/WindowDescriptors.hpp index b35edf15..9964940c 100644 --- a/src/common/WindowDescriptors.hpp +++ b/src/common/WindowDescriptors.hpp @@ -98,7 +98,7 @@ class WindowLayout { public: // A complete window layout has a single emote popup position that is shared among all windows - QPoint emotePopupPos_; + QRect emotePopupBounds_; std::vector windows_; diff --git a/src/singletons/WindowManager.cpp b/src/singletons/WindowManager.cpp index 63bc63b3..69586928 100644 --- a/src/singletons/WindowManager.cpp +++ b/src/singletons/WindowManager.cpp @@ -328,14 +328,18 @@ void WindowManager::scrollToMessage(const MessagePtr &message) this->scrollToMessageSignal.invoke(message); } -QPoint WindowManager::emotePopupPos() +QRect WindowManager::emotePopupBounds() const { - return this->emotePopupPos_; + return this->emotePopupBounds_; } -void WindowManager::setEmotePopupPos(QPoint pos) +void WindowManager::setEmotePopupBounds(QRect bounds) { - this->emotePopupPos_ = pos; + if (this->emotePopupBounds_ != bounds) + { + this->emotePopupBounds_ = bounds; + this->queueSave(); + } } void WindowManager::initialize(Settings &settings, const Paths &paths) @@ -371,7 +375,7 @@ void WindowManager::initialize(Settings &settings, const Paths &paths) windowLayout.activateOrAddChannel(desired->provider, desired->name); } - this->emotePopupPos_ = windowLayout.emotePopupPos_; + this->emotePopupBounds_ = windowLayout.emotePopupBounds_; this->applyWindowLayout(windowLayout); } @@ -483,10 +487,12 @@ void WindowManager::save() windowObj.insert("width", rect.width()); windowObj.insert("height", rect.height()); - QJsonObject emotePopupObj; - emotePopupObj.insert("x", this->emotePopupPos_.x()); - emotePopupObj.insert("y", this->emotePopupPos_.y()); - windowObj.insert("emotePopup", emotePopupObj); + windowObj["emotePopup"] = QJsonObject{ + {"x", this->emotePopupBounds_.x()}, + {"y", this->emotePopupBounds_.y()}, + {"width", this->emotePopupBounds_.width()}, + {"height", this->emotePopupBounds_.height()}, + }; // window tabs QJsonArray tabsArr; @@ -753,7 +759,7 @@ void WindowManager::applyWindowLayout(const WindowLayout &layout) } // Set emote popup position - this->emotePopupPos_ = layout.emotePopupPos_; + this->emotePopupBounds_ = layout.emotePopupBounds_; for (const auto &windowData : layout.windows_) { @@ -802,10 +808,14 @@ void WindowManager::applyWindowLayout(const WindowLayout &layout) // Have to offset x by one because qt moves the window 1px too // far to the left:w - window.setInitialBounds({windowData.geometry_.x(), - windowData.geometry_.y(), - windowData.geometry_.width(), - windowData.geometry_.height()}); + window.setInitialBounds( + { + windowData.geometry_.x(), + windowData.geometry_.y(), + windowData.geometry_.width(), + windowData.geometry_.height(), + }, + widgets::BoundsChecking::Off); } } diff --git a/src/singletons/WindowManager.hpp b/src/singletons/WindowManager.hpp index 927a5712..21040fab 100644 --- a/src/singletons/WindowManager.hpp +++ b/src/singletons/WindowManager.hpp @@ -99,8 +99,8 @@ public: */ void scrollToMessage(const MessagePtr &message); - QPoint emotePopupPos(); - void setEmotePopupPos(QPoint pos); + QRect emotePopupBounds() const; + void setEmotePopupBounds(QRect bounds); void initialize(Settings &settings, const Paths &paths) override; void save() override; @@ -154,7 +154,7 @@ private: bool initialized_ = false; bool shuttingDown_ = false; - QPoint emotePopupPos_; + QRect emotePopupBounds_; std::atomic generation_{0}; diff --git a/src/util/WidgetHelpers.cpp b/src/util/WidgetHelpers.cpp index b5e6fa9a..cd76dea8 100644 --- a/src/util/WidgetHelpers.cpp +++ b/src/util/WidgetHelpers.cpp @@ -8,8 +8,7 @@ namespace { -/// Move the `window` into the `screen` geometry if it's not already in there. -void moveWithinScreen(QWidget *window, QScreen *screen, QPoint point) +QPoint applyBounds(QScreen *screen, QPoint point, QSize frameSize, int height) { if (screen == nullptr) { @@ -21,9 +20,6 @@ void moveWithinScreen(QWidget *window, QScreen *screen, QPoint point) 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()); @@ -32,30 +28,72 @@ void moveWithinScreen(QWidget *window, QScreen *screen, QPoint point) { point.setY(bounds.top()); } - if (point.x() + w > bounds.right()) + if (point.x() + frameSize.width() > bounds.right()) { stickRight = true; - point.setX(bounds.right() - w); + point.setX(bounds.right() - frameSize.width()); } - if (point.y() + h > bounds.bottom()) + if (point.y() + frameSize.height() > bounds.bottom()) { stickBottom = true; - point.setY(bounds.bottom() - h); + point.setY(bounds.bottom() - frameSize.height()); } if (stickRight && stickBottom) { const QPoint globalCursorPos = QCursor::pos(); - point.setY(globalCursorPos.y() - window->height() - 16); + point.setY(globalCursorPos.y() - height - 16); } - window->move(point); + return point; +} + +/// Move the `window` into the `screen` geometry if it's not already in there. +void moveWithinScreen(QWidget *window, QScreen *screen, QPoint point) +{ + auto checked = + applyBounds(screen, point, window->frameSize(), window->height()); + window->move(checked); } } // namespace namespace chatterino::widgets { +QRect checkInitialBounds(QRect initialBounds, BoundsChecking mode) +{ + switch (mode) + { + case BoundsChecking::Off: { + return initialBounds; + } + break; + + case BoundsChecking::CursorPosition: { + return QRect{ + applyBounds(QGuiApplication::screenAt(QCursor::pos()), + initialBounds.topLeft(), initialBounds.size(), + initialBounds.height()), + initialBounds.size(), + }; + } + break; + + case BoundsChecking::DesiredPosition: { + return QRect{ + applyBounds(QGuiApplication::screenAt(initialBounds.topLeft()), + initialBounds.topLeft(), initialBounds.size(), + initialBounds.height()), + initialBounds.size(), + }; + } + break; + default: + assert(false && "Invalid bounds checking mode"); + return initialBounds; + } +} + void moveWindowTo(QWidget *window, QPoint position, BoundsChecking mode) { switch (mode) diff --git a/src/util/WidgetHelpers.hpp b/src/util/WidgetHelpers.hpp index b09e93d0..5de90340 100644 --- a/src/util/WidgetHelpers.hpp +++ b/src/util/WidgetHelpers.hpp @@ -3,6 +3,7 @@ class QWidget; class QPoint; class QScreen; +class QRect; namespace chatterino::widgets { @@ -17,6 +18,14 @@ enum class BoundsChecking { DesiredPosition, }; +/// Applies bounds checking to @a initialBounds. +/// +/// @param initialBounds The bounds to check. +/// @param mode The desired bounds checking. +/// @returns The potentially modified bounds. +QRect checkInitialBounds(QRect initialBounds, + BoundsChecking mode = BoundsChecking::DesiredPosition); + /// Moves the `window` to the (global) `position` /// while doing bounds-checking according to `mode` to ensure the window stays on one screen. /// diff --git a/src/widgets/BaseWindow.cpp b/src/widgets/BaseWindow.cpp index f3a2afa1..6d880067 100644 --- a/src/widgets/BaseWindow.cpp +++ b/src/widgets/BaseWindow.cpp @@ -29,7 +29,9 @@ # pragma comment(lib, "Dwmapi.lib") # include +# include # include +# include #endif #include "widgets/helper/TitlebarButton.hpp" @@ -251,8 +253,9 @@ BaseWindow::~BaseWindow() DebugCount::decrease("BaseWindow"); } -void BaseWindow::setInitialBounds(const QRect &bounds) +void BaseWindow::setInitialBounds(QRect bounds, widgets::BoundsChecking mode) { + bounds = widgets::checkInitialBounds(bounds, mode); #ifdef USEWINSDK this->initalBounds_ = bounds; #else @@ -260,7 +263,7 @@ void BaseWindow::setInitialBounds(const QRect &bounds) #endif } -QRect BaseWindow::getBounds() +QRect BaseWindow::getBounds() const { #ifdef USEWINSDK return this->currentBounds_; @@ -444,7 +447,7 @@ QWidget *BaseWindow::getLayoutContainer() } } -bool BaseWindow::hasCustomWindowFrame() +bool BaseWindow::hasCustomWindowFrame() const { return BaseWindow::supportsCustomWindowFrame() && this->enableCustomFrame_; } diff --git a/src/widgets/BaseWindow.hpp b/src/widgets/BaseWindow.hpp index 46ff56e7..946c9e24 100644 --- a/src/widgets/BaseWindow.hpp +++ b/src/widgets/BaseWindow.hpp @@ -45,11 +45,11 @@ public: QWidget *parent = nullptr); ~BaseWindow() override; - void setInitialBounds(const QRect &bounds); - QRect getBounds(); + void setInitialBounds(QRect bounds, widgets::BoundsChecking mode); + QRect getBounds() const; QWidget *getLayoutContainer(); - bool hasCustomWindowFrame(); + bool hasCustomWindowFrame() const; TitleBarButton *addTitleBarButton(const TitleBarButtonStyle &style, std::function onClicked); EffectLabel *addTitleBarLabel(std::function onClicked); diff --git a/src/widgets/dialogs/EmotePopup.cpp b/src/widgets/dialogs/EmotePopup.cpp index fd80d7e9..989233ff 100644 --- a/src/widgets/dialogs/EmotePopup.cpp +++ b/src/widgets/dialogs/EmotePopup.cpp @@ -203,13 +203,18 @@ EmoteMap filterEmoteMap(const QString &text, namespace chatterino { EmotePopup::EmotePopup(QWidget *parent) - : BasePopup(BaseWindow::EnableCustomFrame, parent) + : BasePopup({BaseWindow::EnableCustomFrame, BaseWindow::DisableLayoutSave}, + parent) , search_(new QLineEdit()) , notebook_(new Notebook(this)) { // this->setStayInScreenRect(true); - this->moveTo(getIApp()->getWindows()->emotePopupPos(), - widgets::BoundsChecking::DesiredPosition); + auto bounds = getIApp()->getWindows()->emotePopupBounds(); + if (bounds.size().isEmpty()) + { + bounds.setSize(QSize{300, 500} * this->scale()); + } + this->setInitialBounds(bounds, widgets::BoundsChecking::DesiredPosition); auto *layout = new QVBoxLayout(); this->getLayoutContainer()->setLayout(layout); @@ -594,10 +599,27 @@ void EmotePopup::filterEmotes(const QString &searchText) this->searchView_->show(); } +void EmotePopup::saveBounds() const +{ + getIApp()->getWindows()->setEmotePopupBounds(this->getBounds()); +} + +void EmotePopup::resizeEvent(QResizeEvent *event) +{ + this->saveBounds(); + BasePopup::resizeEvent(event); +} + +void EmotePopup::moveEvent(QMoveEvent *event) +{ + this->saveBounds(); + BasePopup::moveEvent(event); +} + void EmotePopup::closeEvent(QCloseEvent *event) { - getIApp()->getWindows()->setEmotePopupPos(this->pos()); - BaseWindow::closeEvent(event); + this->saveBounds(); + BasePopup::closeEvent(event); } } // namespace chatterino diff --git a/src/widgets/dialogs/EmotePopup.hpp b/src/widgets/dialogs/EmotePopup.hpp index 43dbb45c..85b5aa5c 100644 --- a/src/widgets/dialogs/EmotePopup.hpp +++ b/src/widgets/dialogs/EmotePopup.hpp @@ -25,6 +25,10 @@ public: pajlada::Signals::Signal linkClicked; +protected: + void resizeEvent(QResizeEvent *event) override; + void moveEvent(QMoveEvent *event) override; + private: ChannelView *globalEmotesView_{}; ChannelView *channelEmotesView_{}; @@ -47,6 +51,8 @@ private: void filterEmotes(const QString &text); void addShortcuts() override; bool eventFilter(QObject *object, QEvent *event) override; + + void saveBounds() const; }; } // namespace chatterino diff --git a/src/widgets/splits/SplitInput.cpp b/src/widgets/splits/SplitInput.cpp index d53b4bad..141eeed5 100644 --- a/src/widgets/splits/SplitInput.cpp +++ b/src/widgets/splits/SplitInput.cpp @@ -309,8 +309,6 @@ void SplitInput::openEmotePopup() }); } - this->emotePopup_->resize(int(300 * this->emotePopup_->scale()), - int(500 * this->emotePopup_->scale())); this->emotePopup_->loadChannel(this->split_->getChannel()); this->emotePopup_->show(); this->emotePopup_->raise();