diff --git a/CHANGELOG.md b/CHANGELOG.md
index e08c055f..5f7b4f66 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@
- Major: Added multi-channel searching to search dialog via keyboard shortcut. (Ctrl+Shift+F by default) (#3694, #3875)
- Major: Added support for emotes and badges from [7TV](https://7tv.app). [Wiki Page](https://wiki.chatterino.com/Third_party_services/#7tv) (#4002, #4062)
- Major: Added support for Right-to-Left Languages (#3958, #4139)
+- Minor: Added ability to pin Usercards to stay open even if it loses focus. Only available if "Automatically close usercard when it loses focus" is enabled. (#3884)
- Minor: Allow hiding moderation actions in streamer mode. (#3926)
- Minor: Added highlights for `Elevated Messages`. (#4016)
- Minor: Removed total views from the usercard, as Twitch no longer updates the number. (#3792)
diff --git a/resources/buttons/pinDisabledDark.png b/resources/buttons/pinDisabledDark.png
new file mode 100644
index 00000000..f762f0ad
Binary files /dev/null and b/resources/buttons/pinDisabledDark.png differ
diff --git a/resources/buttons/pinDisabledDark.svg b/resources/buttons/pinDisabledDark.svg
new file mode 100644
index 00000000..373864e0
--- /dev/null
+++ b/resources/buttons/pinDisabledDark.svg
@@ -0,0 +1,23 @@
+
+
+
+
diff --git a/resources/buttons/pinDisabledLight.png b/resources/buttons/pinDisabledLight.png
new file mode 100644
index 00000000..ed381899
Binary files /dev/null and b/resources/buttons/pinDisabledLight.png differ
diff --git a/resources/buttons/pinDisabledLight.svg b/resources/buttons/pinDisabledLight.svg
new file mode 100644
index 00000000..eb086e43
--- /dev/null
+++ b/resources/buttons/pinDisabledLight.svg
@@ -0,0 +1,23 @@
+
+
+
+
diff --git a/resources/buttons/pinEnabled.png b/resources/buttons/pinEnabled.png
new file mode 100644
index 00000000..5a113da1
Binary files /dev/null and b/resources/buttons/pinEnabled.png differ
diff --git a/resources/buttons/pinEnabled.svg b/resources/buttons/pinEnabled.svg
new file mode 100644
index 00000000..412d1c99
--- /dev/null
+++ b/resources/buttons/pinEnabled.svg
@@ -0,0 +1,23 @@
+
+
+
+
diff --git a/resources/resources_autogenerated.qrc b/resources/resources_autogenerated.qrc
index c7a02e62..4d84f4fd 100644
--- a/resources/resources_autogenerated.qrc
+++ b/resources/resources_autogenerated.qrc
@@ -38,6 +38,12 @@
buttons/modModeDisabled2.png
buttons/modModeEnabled.png
buttons/modModeEnabled2.png
+ buttons/pinDisabledDark.png
+ buttons/pinDisabledDark.svg
+ buttons/pinDisabledLight.png
+ buttons/pinDisabledLight.svg
+ buttons/pinEnabled.png
+ buttons/pinEnabled.svg
buttons/replyDark.png
buttons/replyDark.svg
buttons/replyThreadDark.png
diff --git a/src/autogenerated/ResourcesAutogen.cpp b/src/autogenerated/ResourcesAutogen.cpp
index 671376e6..d5a718af 100644
--- a/src/autogenerated/ResourcesAutogen.cpp
+++ b/src/autogenerated/ResourcesAutogen.cpp
@@ -34,6 +34,9 @@ Resources2::Resources2()
this->buttons.modModeDisabled2 = QPixmap(":/buttons/modModeDisabled2.png");
this->buttons.modModeEnabled = QPixmap(":/buttons/modModeEnabled.png");
this->buttons.modModeEnabled2 = QPixmap(":/buttons/modModeEnabled2.png");
+ this->buttons.pinDisabledDark = QPixmap(":/buttons/pinDisabledDark.png");
+ this->buttons.pinDisabledLight = QPixmap(":/buttons/pinDisabledLight.png");
+ this->buttons.pinEnabled = QPixmap(":/buttons/pinEnabled.png");
this->buttons.replyDark = QPixmap(":/buttons/replyDark.png");
this->buttons.replyThreadDark = QPixmap(":/buttons/replyThreadDark.png");
this->buttons.search = QPixmap(":/buttons/search.png");
diff --git a/src/autogenerated/ResourcesAutogen.hpp b/src/autogenerated/ResourcesAutogen.hpp
index 218d5953..200a3fbd 100644
--- a/src/autogenerated/ResourcesAutogen.hpp
+++ b/src/autogenerated/ResourcesAutogen.hpp
@@ -41,6 +41,9 @@ public:
QPixmap modModeDisabled2;
QPixmap modModeEnabled;
QPixmap modModeEnabled2;
+ QPixmap pinDisabledDark;
+ QPixmap pinDisabledLight;
+ QPixmap pinEnabled;
QPixmap replyDark;
QPixmap replyThreadDark;
QPixmap search;
diff --git a/src/singletons/Theme.cpp b/src/singletons/Theme.cpp
index 635f31b0..e13df1cd 100644
--- a/src/singletons/Theme.cpp
+++ b/src/singletons/Theme.cpp
@@ -278,10 +278,12 @@ void Theme::actuallyUpdate(double hue, double multiplier)
if (this->isLightTheme())
{
this->buttons.copy = getResources().buttons.copyDark;
+ this->buttons.pin = getResources().buttons.pinDisabledDark;
}
else
{
this->buttons.copy = getResources().buttons.copyLight;
+ this->buttons.pin = getResources().buttons.pinDisabledLight;
}
}
diff --git a/src/singletons/Theme.hpp b/src/singletons/Theme.hpp
index ea49457e..bc8b4a10 100644
--- a/src/singletons/Theme.hpp
+++ b/src/singletons/Theme.hpp
@@ -129,6 +129,7 @@ public:
struct {
QPixmap copy;
+ QPixmap pin;
} buttons;
void normalizeColor(QColor &color);
diff --git a/src/widgets/dialogs/UserInfoPopup.cpp b/src/widgets/dialogs/UserInfoPopup.cpp
index 478a0eea..2dd28e29 100644
--- a/src/widgets/dialogs/UserInfoPopup.cpp
+++ b/src/widgets/dialogs/UserInfoPopup.cpp
@@ -134,11 +134,13 @@ UserInfoPopup::UserInfoPopup(bool closeAutomatically, QWidget *parent,
Split *split)
: DraggablePopup(closeAutomatically, parent)
, split_(split)
+ , closeAutomatically_(closeAutomatically)
{
assert(split != nullptr &&
"split being nullptr causes lots of bugs down the road");
this->setWindowTitle("Usercard");
this->setStayInScreenRect(true);
+ this->updateFocusLoss();
HotkeyController::HotkeyMap actions{
{"delete",
@@ -349,6 +351,22 @@ UserInfoPopup::UserInfoPopup(bool closeAutomatically, QWidget *parent,
this->ui_.localizedNameLabel->setVisible(false);
this->ui_.localizedNameCopyButton->setVisible(false);
+
+ // button to pin the window (only if we close automatically)
+ if (this->closeAutomatically_)
+ {
+ this->ui_.pinButton = box.emplace