mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
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:
parent
070151fbc8
commit
06b28ea0ab
|
@ -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)
|
||||||
|
|
BIN
resources/buttons/pinDisabledDark.png
Normal file
BIN
resources/buttons/pinDisabledDark.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 996 B |
23
resources/buttons/pinDisabledDark.svg
Normal file
23
resources/buttons/pinDisabledDark.svg
Normal 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 |
BIN
resources/buttons/pinDisabledLight.png
Normal file
BIN
resources/buttons/pinDisabledLight.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 989 B |
23
resources/buttons/pinDisabledLight.svg
Normal file
23
resources/buttons/pinDisabledLight.svg
Normal 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 |
BIN
resources/buttons/pinEnabled.png
Normal file
BIN
resources/buttons/pinEnabled.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
23
resources/buttons/pinEnabled.svg
Normal file
23
resources/buttons/pinEnabled.svg
Normal 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 |
|
@ -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>
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -129,6 +129,7 @@ public:
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
QPixmap copy;
|
QPixmap copy;
|
||||||
|
QPixmap pin;
|
||||||
} buttons;
|
} buttons;
|
||||||
|
|
||||||
void normalizeColor(QColor &color);
|
void normalizeColor(QColor &color);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Reference in a new issue