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: 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)

View file

@ -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));
}

View file

@ -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<WindowDescriptor> windows_;

View file

@ -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);
}
}

View file

@ -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<int> generation_{0};

View file

@ -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)

View file

@ -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.
///

View file

@ -29,7 +29,9 @@
# pragma comment(lib, "Dwmapi.lib")
# include <QHBoxLayout>
# include <QMargins>
# include <QOperatingSystemVersion>
# include <QWindow>
#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_;
}

View file

@ -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<void()> onClicked);
EffectLabel *addTitleBarLabel(std::function<void()> onClicked);

View file

@ -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

View file

@ -25,6 +25,10 @@ public:
pajlada::Signals::Signal<Link> 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

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