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
This commit is contained in:
nerix 2024-06-01 12:38:39 +02:00 committed by GitHub
parent c3bb99eb01
commit 65bfec963b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 138 additions and 45 deletions

View file

@ -7,6 +7,7 @@
- Minor: Add option to customise Moderation buttons with images. (#5369) - 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: 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: 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) - Minor: Added the ability to duplicate tabs. (#5277)
- Bugfix: Fixed tab move animation occasionally failing to start after closing a tab. (#5426) - 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) - Bugfix: If a network request errors with 200 OK, Qt's error code is now reported instead of the HTTP status. (#5378)

View file

@ -219,9 +219,15 @@ WindowLayout WindowLayout::loadFromFile(const QString &path)
} }
// Load emote popup position // Load emote popup position
QJsonObject emote_popup_obj = windowObj.value("emotePopup").toObject(); {
layout.emotePopupPos_ = QPoint(emote_popup_obj.value("x").toInt(), auto emotePopup = windowObj["emotePopup"].toObject();
emote_popup_obj.value("y").toInt()); layout.emotePopupBounds_ = QRect{
emotePopup["x"].toInt(),
emotePopup["y"].toInt(),
emotePopup["width"].toInt(),
emotePopup["height"].toInt(),
};
}
layout.windows_.emplace_back(std::move(window)); layout.windows_.emplace_back(std::move(window));
} }

View file

@ -98,7 +98,7 @@ class WindowLayout
{ {
public: public:
// A complete window layout has a single emote popup position that is shared among all windows // A complete window layout has a single emote popup position that is shared among all windows
QPoint emotePopupPos_; QRect emotePopupBounds_;
std::vector<WindowDescriptor> windows_; std::vector<WindowDescriptor> windows_;

View file

@ -328,14 +328,18 @@ void WindowManager::scrollToMessage(const MessagePtr &message)
this->scrollToMessageSignal.invoke(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) 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); windowLayout.activateOrAddChannel(desired->provider, desired->name);
} }
this->emotePopupPos_ = windowLayout.emotePopupPos_; this->emotePopupBounds_ = windowLayout.emotePopupBounds_;
this->applyWindowLayout(windowLayout); this->applyWindowLayout(windowLayout);
} }
@ -483,10 +487,12 @@ void WindowManager::save()
windowObj.insert("width", rect.width()); windowObj.insert("width", rect.width());
windowObj.insert("height", rect.height()); windowObj.insert("height", rect.height());
QJsonObject emotePopupObj; windowObj["emotePopup"] = QJsonObject{
emotePopupObj.insert("x", this->emotePopupPos_.x()); {"x", this->emotePopupBounds_.x()},
emotePopupObj.insert("y", this->emotePopupPos_.y()); {"y", this->emotePopupBounds_.y()},
windowObj.insert("emotePopup", emotePopupObj); {"width", this->emotePopupBounds_.width()},
{"height", this->emotePopupBounds_.height()},
};
// window tabs // window tabs
QJsonArray tabsArr; QJsonArray tabsArr;
@ -753,7 +759,7 @@ void WindowManager::applyWindowLayout(const WindowLayout &layout)
} }
// Set emote popup position // Set emote popup position
this->emotePopupPos_ = layout.emotePopupPos_; this->emotePopupBounds_ = layout.emotePopupBounds_;
for (const auto &windowData : layout.windows_) 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 // Have to offset x by one because qt moves the window 1px too
// far to the left:w // far to the left:w
window.setInitialBounds({windowData.geometry_.x(), window.setInitialBounds(
windowData.geometry_.y(), {
windowData.geometry_.width(), windowData.geometry_.x(),
windowData.geometry_.height()}); windowData.geometry_.y(),
windowData.geometry_.width(),
windowData.geometry_.height(),
},
widgets::BoundsChecking::Off);
} }
} }

View file

@ -99,8 +99,8 @@ public:
*/ */
void scrollToMessage(const MessagePtr &message); void scrollToMessage(const MessagePtr &message);
QPoint emotePopupPos(); QRect emotePopupBounds() const;
void setEmotePopupPos(QPoint pos); void setEmotePopupBounds(QRect bounds);
void initialize(Settings &settings, const Paths &paths) override; void initialize(Settings &settings, const Paths &paths) override;
void save() override; void save() override;
@ -154,7 +154,7 @@ private:
bool initialized_ = false; bool initialized_ = false;
bool shuttingDown_ = false; bool shuttingDown_ = false;
QPoint emotePopupPos_; QRect emotePopupBounds_;
std::atomic<int> generation_{0}; std::atomic<int> generation_{0};

View file

@ -8,8 +8,7 @@
namespace { namespace {
/// Move the `window` into the `screen` geometry if it's not already in there. QPoint applyBounds(QScreen *screen, QPoint point, QSize frameSize, int height)
void moveWithinScreen(QWidget *window, QScreen *screen, QPoint point)
{ {
if (screen == nullptr) if (screen == nullptr)
{ {
@ -21,9 +20,6 @@ void moveWithinScreen(QWidget *window, QScreen *screen, QPoint point)
bool stickRight = false; bool stickRight = false;
bool stickBottom = false; bool stickBottom = false;
const auto w = window->frameGeometry().width();
const auto h = window->frameGeometry().height();
if (point.x() < bounds.left()) if (point.x() < bounds.left())
{ {
point.setX(bounds.left()); point.setX(bounds.left());
@ -32,30 +28,72 @@ void moveWithinScreen(QWidget *window, QScreen *screen, QPoint point)
{ {
point.setY(bounds.top()); point.setY(bounds.top());
} }
if (point.x() + w > bounds.right()) if (point.x() + frameSize.width() > bounds.right())
{ {
stickRight = true; 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; stickBottom = true;
point.setY(bounds.bottom() - h); point.setY(bounds.bottom() - frameSize.height());
} }
if (stickRight && stickBottom) if (stickRight && stickBottom)
{ {
const QPoint globalCursorPos = QCursor::pos(); 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
namespace chatterino::widgets { 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) void moveWindowTo(QWidget *window, QPoint position, BoundsChecking mode)
{ {
switch (mode) switch (mode)

View file

@ -3,6 +3,7 @@
class QWidget; class QWidget;
class QPoint; class QPoint;
class QScreen; class QScreen;
class QRect;
namespace chatterino::widgets { namespace chatterino::widgets {
@ -17,6 +18,14 @@ enum class BoundsChecking {
DesiredPosition, 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` /// Moves the `window` to the (global) `position`
/// while doing bounds-checking according to `mode` to ensure the window stays on one screen. /// while doing bounds-checking according to `mode` to ensure the window stays on one screen.
/// ///

View file

@ -29,7 +29,9 @@
# pragma comment(lib, "Dwmapi.lib") # pragma comment(lib, "Dwmapi.lib")
# include <QHBoxLayout> # include <QHBoxLayout>
# include <QMargins>
# include <QOperatingSystemVersion> # include <QOperatingSystemVersion>
# include <QWindow>
#endif #endif
#include "widgets/helper/TitlebarButton.hpp" #include "widgets/helper/TitlebarButton.hpp"
@ -251,8 +253,9 @@ BaseWindow::~BaseWindow()
DebugCount::decrease("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 #ifdef USEWINSDK
this->initalBounds_ = bounds; this->initalBounds_ = bounds;
#else #else
@ -260,7 +263,7 @@ void BaseWindow::setInitialBounds(const QRect &bounds)
#endif #endif
} }
QRect BaseWindow::getBounds() QRect BaseWindow::getBounds() const
{ {
#ifdef USEWINSDK #ifdef USEWINSDK
return this->currentBounds_; return this->currentBounds_;
@ -444,7 +447,7 @@ QWidget *BaseWindow::getLayoutContainer()
} }
} }
bool BaseWindow::hasCustomWindowFrame() bool BaseWindow::hasCustomWindowFrame() const
{ {
return BaseWindow::supportsCustomWindowFrame() && this->enableCustomFrame_; return BaseWindow::supportsCustomWindowFrame() && this->enableCustomFrame_;
} }

View file

@ -45,11 +45,11 @@ public:
QWidget *parent = nullptr); QWidget *parent = nullptr);
~BaseWindow() override; ~BaseWindow() override;
void setInitialBounds(const QRect &bounds); void setInitialBounds(QRect bounds, widgets::BoundsChecking mode);
QRect getBounds(); QRect getBounds() const;
QWidget *getLayoutContainer(); QWidget *getLayoutContainer();
bool hasCustomWindowFrame(); bool hasCustomWindowFrame() const;
TitleBarButton *addTitleBarButton(const TitleBarButtonStyle &style, TitleBarButton *addTitleBarButton(const TitleBarButtonStyle &style,
std::function<void()> onClicked); std::function<void()> onClicked);
EffectLabel *addTitleBarLabel(std::function<void()> onClicked); EffectLabel *addTitleBarLabel(std::function<void()> onClicked);

View file

@ -203,13 +203,18 @@ EmoteMap filterEmoteMap(const QString &text,
namespace chatterino { namespace chatterino {
EmotePopup::EmotePopup(QWidget *parent) EmotePopup::EmotePopup(QWidget *parent)
: BasePopup(BaseWindow::EnableCustomFrame, parent) : BasePopup({BaseWindow::EnableCustomFrame, BaseWindow::DisableLayoutSave},
parent)
, search_(new QLineEdit()) , search_(new QLineEdit())
, notebook_(new Notebook(this)) , notebook_(new Notebook(this))
{ {
// this->setStayInScreenRect(true); // this->setStayInScreenRect(true);
this->moveTo(getIApp()->getWindows()->emotePopupPos(), auto bounds = getIApp()->getWindows()->emotePopupBounds();
widgets::BoundsChecking::DesiredPosition); if (bounds.size().isEmpty())
{
bounds.setSize(QSize{300, 500} * this->scale());
}
this->setInitialBounds(bounds, widgets::BoundsChecking::DesiredPosition);
auto *layout = new QVBoxLayout(); auto *layout = new QVBoxLayout();
this->getLayoutContainer()->setLayout(layout); this->getLayoutContainer()->setLayout(layout);
@ -594,10 +599,27 @@ void EmotePopup::filterEmotes(const QString &searchText)
this->searchView_->show(); 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) void EmotePopup::closeEvent(QCloseEvent *event)
{ {
getIApp()->getWindows()->setEmotePopupPos(this->pos()); this->saveBounds();
BaseWindow::closeEvent(event); BasePopup::closeEvent(event);
} }
} // namespace chatterino } // namespace chatterino

View file

@ -25,6 +25,10 @@ public:
pajlada::Signals::Signal<Link> linkClicked; pajlada::Signals::Signal<Link> linkClicked;
protected:
void resizeEvent(QResizeEvent *event) override;
void moveEvent(QMoveEvent *event) override;
private: private:
ChannelView *globalEmotesView_{}; ChannelView *globalEmotesView_{};
ChannelView *channelEmotesView_{}; ChannelView *channelEmotesView_{};
@ -47,6 +51,8 @@ private:
void filterEmotes(const QString &text); void filterEmotes(const QString &text);
void addShortcuts() override; void addShortcuts() override;
bool eventFilter(QObject *object, QEvent *event) override; bool eventFilter(QObject *object, QEvent *event) override;
void saveBounds() const;
}; };
} // namespace chatterino } // namespace chatterino

View file

@ -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_->loadChannel(this->split_->getChannel());
this->emotePopup_->show(); this->emotePopup_->show();
this->emotePopup_->raise(); this->emotePopup_->raise();