Add ability to pin Usercards to stay open even if it loses focus (#3884)

Co-authored-by: pajlada <rasmus.karlsson@pajlada.com>
Co-authored-by: Rasmus Karlsson <rasmus.karlsson@pajlada.com>
Co-authored-by: James Upjohn <jammehcow@jammehcow.co.nz>
This commit is contained in:
Patrick Geneva 2022-11-12 07:21:43 -05:00 committed by GitHub
parent 070151fbc8
commit 06b28ea0ab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 134 additions and 1 deletions

View file

@ -6,6 +6,7 @@
- Major: Added multi-channel searching to search dialog via keyboard shortcut. (Ctrl+Shift+F by default) (#3694, #3875) - 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 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) - 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: Allow hiding moderation actions in streamer mode. (#3926)
- Minor: Added highlights for `Elevated Messages`. (#4016) - Minor: Added highlights for `Elevated Messages`. (#4016)
- Minor: Removed total views from the usercard, as Twitch no longer updates the number. (#3792) - Minor: Removed total views from the usercard, as Twitch no longer updates the number. (#3792)

Binary file not shown.

After

Width:  |  Height:  |  Size: 996 B

View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="15.999924mm"
height="15.999949mm"
viewBox="0 0 15.999924 15.999949"
version="1.1"
id="svg5"
xml:space="preserve"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs2" /><g
id="layer2"
transform="translate(-4.439446e-5,3.0446178e-5)"><path
class="UnoptimicedTransforms"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 12.435417,-0.52916667 3.96875,3.96874997 V 3.96875 l -4.7625,4.7625 V 11.1125 L 11.1125,11.641667 H 10.847917 L 4.2333333,5.0270833 V 4.7625 L 4.7625,4.2333333 h 2.38125 l 4.7625,-4.76249997 z"
id="path234"
transform="matrix(1.0300653,0,0,1.0300668,-1.0336639,0.68131565)" /><path
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.272538px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 5.098444,9.8113564 6.1885966,10.901511 1.2829102,15.807204 0.19275776,14.717051 Z"
id="path1157" /></g></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 989 B

View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="15.999924mm"
height="15.999949mm"
viewBox="0 0 15.999924 15.999949"
version="1.1"
id="svg5"
xml:space="preserve"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs2" /><g
id="layer2"
transform="translate(-4.439446e-5,3.0446178e-5)"><path
class="UnoptimicedTransforms"
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 12.435417,-0.52916667 3.96875,3.96874997 V 3.96875 l -4.7625,4.7625 V 11.1125 L 11.1125,11.641667 H 10.847917 L 4.2333333,5.0270833 V 4.7625 L 4.7625,4.2333333 h 2.38125 l 4.7625,-4.76249997 z"
id="path234"
transform="matrix(1.0300653,0,0,1.0300668,-1.0336639,0.68131565)" /><path
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.272538px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 5.098444,9.8113564 6.1885966,10.901511 1.2829102,15.807204 0.19275776,14.717051 Z"
id="path1157" /></g></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="15.999924mm"
height="15.999949mm"
viewBox="0 0 15.999924 15.999949"
version="1.1"
id="svg5"
xml:space="preserve"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs2" /><g
id="layer2"
transform="translate(-4.439446e-5,3.0446178e-5)"><path
class="UnoptimicedTransforms"
style="fill:#e25c41;fill-opacity:1;stroke:#e25c41;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 12.435417,-0.52916667 3.96875,3.96874997 V 3.96875 l -4.7625,4.7625 V 11.1125 L 11.1125,11.641667 H 10.847917 L 4.2333333,5.0270833 V 4.7625 L 4.7625,4.2333333 h 2.38125 l 4.7625,-4.76249997 z"
id="path234"
transform="matrix(1.0300653,0,0,1.0300668,-1.0336639,0.68131565)" /><path
style="fill:#e25c41;fill-opacity:1;stroke:#e25c41;stroke-width:0.272538px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 5.098444,9.8113564 6.1885966,10.901511 1.2829102,15.807204 0.19275776,14.717051 Z"
id="path1157" /></g></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -38,6 +38,12 @@
<file>buttons/modModeDisabled2.png</file> <file>buttons/modModeDisabled2.png</file>
<file>buttons/modModeEnabled.png</file> <file>buttons/modModeEnabled.png</file>
<file>buttons/modModeEnabled2.png</file> <file>buttons/modModeEnabled2.png</file>
<file>buttons/pinDisabledDark.png</file>
<file>buttons/pinDisabledDark.svg</file>
<file>buttons/pinDisabledLight.png</file>
<file>buttons/pinDisabledLight.svg</file>
<file>buttons/pinEnabled.png</file>
<file>buttons/pinEnabled.svg</file>
<file>buttons/replyDark.png</file> <file>buttons/replyDark.png</file>
<file>buttons/replyDark.svg</file> <file>buttons/replyDark.svg</file>
<file>buttons/replyThreadDark.png</file> <file>buttons/replyThreadDark.png</file>

View file

@ -34,6 +34,9 @@ Resources2::Resources2()
this->buttons.modModeDisabled2 = QPixmap(":/buttons/modModeDisabled2.png"); this->buttons.modModeDisabled2 = QPixmap(":/buttons/modModeDisabled2.png");
this->buttons.modModeEnabled = QPixmap(":/buttons/modModeEnabled.png"); this->buttons.modModeEnabled = QPixmap(":/buttons/modModeEnabled.png");
this->buttons.modModeEnabled2 = QPixmap(":/buttons/modModeEnabled2.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.replyDark = QPixmap(":/buttons/replyDark.png");
this->buttons.replyThreadDark = QPixmap(":/buttons/replyThreadDark.png"); this->buttons.replyThreadDark = QPixmap(":/buttons/replyThreadDark.png");
this->buttons.search = QPixmap(":/buttons/search.png"); this->buttons.search = QPixmap(":/buttons/search.png");

View file

@ -41,6 +41,9 @@ public:
QPixmap modModeDisabled2; QPixmap modModeDisabled2;
QPixmap modModeEnabled; QPixmap modModeEnabled;
QPixmap modModeEnabled2; QPixmap modModeEnabled2;
QPixmap pinDisabledDark;
QPixmap pinDisabledLight;
QPixmap pinEnabled;
QPixmap replyDark; QPixmap replyDark;
QPixmap replyThreadDark; QPixmap replyThreadDark;
QPixmap search; QPixmap search;

View file

@ -278,10 +278,12 @@ void Theme::actuallyUpdate(double hue, double multiplier)
if (this->isLightTheme()) if (this->isLightTheme())
{ {
this->buttons.copy = getResources().buttons.copyDark; this->buttons.copy = getResources().buttons.copyDark;
this->buttons.pin = getResources().buttons.pinDisabledDark;
} }
else else
{ {
this->buttons.copy = getResources().buttons.copyLight; this->buttons.copy = getResources().buttons.copyLight;
this->buttons.pin = getResources().buttons.pinDisabledLight;
} }
} }

View file

@ -129,6 +129,7 @@ public:
struct { struct {
QPixmap copy; QPixmap copy;
QPixmap pin;
} buttons; } buttons;
void normalizeColor(QColor &color); void normalizeColor(QColor &color);

View file

@ -134,11 +134,13 @@ UserInfoPopup::UserInfoPopup(bool closeAutomatically, QWidget *parent,
Split *split) Split *split)
: DraggablePopup(closeAutomatically, parent) : DraggablePopup(closeAutomatically, parent)
, split_(split) , split_(split)
, closeAutomatically_(closeAutomatically)
{ {
assert(split != nullptr && assert(split != nullptr &&
"split being nullptr causes lots of bugs down the road"); "split being nullptr causes lots of bugs down the road");
this->setWindowTitle("Usercard"); this->setWindowTitle("Usercard");
this->setStayInScreenRect(true); this->setStayInScreenRect(true);
this->updateFocusLoss();
HotkeyController::HotkeyMap actions{ HotkeyController::HotkeyMap actions{
{"delete", {"delete",
@ -349,6 +351,22 @@ UserInfoPopup::UserInfoPopup(bool closeAutomatically, QWidget *parent,
this->ui_.localizedNameLabel->setVisible(false); this->ui_.localizedNameLabel->setVisible(false);
this->ui_.localizedNameCopyButton->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<Button>().getElement();
this->ui_.pinButton->setPixmap(
getApp()->themes->buttons.pin);
this->ui_.pinButton->setScaleIndependantSize(18, 18);
this->ui_.pinButton->setToolTip("Pin Window");
QObject::connect(this->ui_.pinButton, &Button::leftClicked,
[this]() {
this->closeAutomatically_ =
!this->closeAutomatically_;
this->updateFocusLoss();
});
}
} }
// items on the left // items on the left
@ -873,6 +891,26 @@ void UserInfoPopup::updateUserData()
this->ui_.ignoreHighlights->setEnabled(false); this->ui_.ignoreHighlights->setEnabled(false);
} }
void UserInfoPopup::updateFocusLoss()
{
if (this->closeAutomatically_)
{
this->setActionOnFocusLoss(BaseWindow::Delete);
if (this->ui_.pinButton != nullptr)
{
this->ui_.pinButton->setPixmap(getApp()->themes->buttons.pin);
}
}
else
{
this->setActionOnFocusLoss(BaseWindow::Nothing);
if (this->ui_.pinButton != nullptr)
{
this->ui_.pinButton->setPixmap(getResources().buttons.pinEnabled);
}
}
}
void UserInfoPopup::loadAvatar(const QUrl &url) void UserInfoPopup::loadAvatar(const QUrl &url)
{ {
QNetworkRequest req(url); QNetworkRequest req(url);

View file

@ -36,6 +36,7 @@ private:
void installEvents(); void installEvents();
void updateUserData(); void updateUserData();
void updateLatestMessages(); void updateLatestMessages();
void updateFocusLoss();
void loadAvatar(const QUrl &url); void loadAvatar(const QUrl &url);
bool isMod_; bool isMod_;
@ -46,8 +47,10 @@ private:
QString userName_; QString userName_;
QString userId_; QString userId_;
QString avatarUrl_; QString avatarUrl_;
// The channel the popup was opened from (e.g. /mentions or #forsen). Can be a special channel. // The channel the popup was opened from (e.g. /mentions or #forsen). Can be a special channel.
ChannelPtr channel_; ChannelPtr channel_;
// The channel the messages are rendered from (e.g. #forsen). Can be a special channel, but will try to not be where possible. // The channel the messages are rendered from (e.g. #forsen). Can be a special channel, but will try to not be where possible.
ChannelPtr underlyingChannel_; ChannelPtr underlyingChannel_;
@ -55,6 +58,11 @@ private:
std::unique_ptr<pajlada::Signals::ScopedConnection> refreshConnection_; std::unique_ptr<pajlada::Signals::ScopedConnection> refreshConnection_;
// If we should close the dialog automatically if the user clicks out
// Initially set based on the "Automatically close usercard when it loses focus" setting
// If that setting is enabled, this can be toggled on and off using the pin in the top-right corner
bool closeAutomatically_;
struct { struct {
Button *avatarButton = nullptr; Button *avatarButton = nullptr;
Button *localizedNameCopyButton = nullptr; Button *localizedNameCopyButton = nullptr;
@ -64,6 +72,8 @@ private:
Label *followerCountLabel = nullptr; Label *followerCountLabel = nullptr;
Label *createdDateLabel = nullptr; Label *createdDateLabel = nullptr;
Label *userIDLabel = nullptr; Label *userIDLabel = nullptr;
// Can be uninitialized if usercard is not configured to close on focus loss
Button *pinButton = nullptr;
Label *followageLabel = nullptr; Label *followageLabel = nullptr;
Label *subageLabel = nullptr; Label *subageLabel = nullptr;

View file

@ -700,7 +700,7 @@ void GeneralPage::initLayout(GeneralPageView &layout)
s.mentionUsersWithComma); s.mentionUsersWithComma);
layout.addCheckbox("Show joined users (< 1000 chatters)", s.showJoins); layout.addCheckbox("Show joined users (< 1000 chatters)", s.showJoins);
layout.addCheckbox("Show parted users (< 1000 chatters)", s.showParts); layout.addCheckbox("Show parted users (< 1000 chatters)", s.showParts);
layout.addCheckbox("Automatically close user popup when it loses focus", layout.addCheckbox("Automatically close usercard when it loses focus",
s.autoCloseUserPopup); s.autoCloseUserPopup);
layout.addCheckbox( layout.addCheckbox(
"Automatically close reply thread popup when it loses focus", "Automatically close reply thread popup when it loses focus",