Merge remote-tracking branch 'main_repo/master' into git_is_pepega

This commit is contained in:
Mm2PL
2020-04-18 13:39:01 +02:00
155 changed files with 3182 additions and 2037 deletions
-2
View File
@@ -29,8 +29,6 @@ BOOL CALLBACK enumWindows(HWND hwnd, LPARAM)
auto className = std::make_unique<WCHAR[]>(length);
GetClassName(hwnd, className.get(), length);
// qDebug() << QString::fromWCharArray(className.get(), length);
if (lstrcmp(className.get(), L"Shell_TrayWnd") == 0 ||
lstrcmp(className.get(), L"Shell_Secondary") == 0)
{
-2
View File
@@ -384,7 +384,6 @@ void BaseWindow::mousePressEvent(QMouseEvent *event)
if (!recursiveCheckMouseTracking(widget))
{
qDebug() << "Start moving";
this->moving = true;
}
}
@@ -401,7 +400,6 @@ void BaseWindow::mouseReleaseEvent(QMouseEvent *event)
{
if (this->moving)
{
qDebug() << "Stop moving";
this->moving = false;
}
}
-1
View File
@@ -149,7 +149,6 @@ void Notebook::select(QWidget *page)
{
if (containsChild(page, item.selectedWidget))
{
qDebug() << item.selectedWidget;
item.selectedWidget->setFocus(Qt::MouseFocusReason);
}
else
+22 -14
View File
@@ -3,6 +3,7 @@
#include "Application.hpp"
#include "singletons/Settings.hpp"
#include "singletons/Theme.hpp"
#include "singletons/WindowManager.hpp"
#include "widgets/helper/ChannelView.hpp"
#include <QDebug>
@@ -242,6 +243,8 @@ void Scrollbar::paintEvent(QPaintEvent *)
QPainter painter(this);
painter.fillRect(rect(), this->theme->scrollbars.background);
bool enableRedeemedHighlights = getSettings()->enableRedeemedHighlight;
// painter.fillRect(QRect(xOffset, 0, width(), this->buttonHeight),
// this->themeManager->ScrollbarArrow);
// painter.fillRect(QRect(xOffset, height() - this->buttonHeight,
@@ -274,7 +277,8 @@ void Scrollbar::paintEvent(QPaintEvent *)
int w = this->width();
float y = 0;
float dY = float(this->height()) / float(snapshotLength);
int highlightHeight = int(std::ceil(dY));
int highlightHeight =
int(std::ceil(std::max<float>(this->scale() * 2, dY)));
for (size_t i = 0; i < snapshotLength; i++)
{
@@ -282,22 +286,26 @@ void Scrollbar::paintEvent(QPaintEvent *)
if (!highlight.isNull())
{
QColor color = highlight.getColor();
switch (highlight.getStyle())
if (!highlight.isRedeemedHighlight() || enableRedeemedHighlights)
{
case ScrollbarHighlight::Default: {
painter.fillRect(w / 8 * 3, int(y), w / 4, highlightHeight,
color);
}
break;
QColor color = highlight.getColor();
color.setAlpha(255);
case ScrollbarHighlight::Line: {
painter.fillRect(0, int(y), w, 1, color);
}
break;
switch (highlight.getStyle())
{
case ScrollbarHighlight::Default: {
painter.fillRect(w / 8 * 3, int(y), w / 4,
highlightHeight, color);
}
break;
case ScrollbarHighlight::None:;
case ScrollbarHighlight::Line: {
painter.fillRect(0, int(y), w, 1, color);
}
break;
case ScrollbarHighlight::None:;
}
}
}
+11 -1
View File
@@ -1,12 +1,14 @@
#include "EmotePopup.hpp"
#include "Application.hpp"
#include "common/CompletionModel.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "debug/Benchmark.hpp"
#include "messages/Message.hpp"
#include "messages/MessageBuilder.hpp"
#include "providers/twitch/TwitchChannel.hpp"
#include "singletons/Emotes.hpp"
#include "singletons/WindowManager.hpp"
#include "util/Shortcut.hpp"
#include "widgets/Notebook.hpp"
#include "widgets/helper/ChannelView.hpp"
@@ -37,7 +39,8 @@ namespace {
std::sort(vec.begin(), vec.end(),
[](const std::pair<EmoteName, EmotePtr> &l,
const std::pair<EmoteName, EmotePtr> &r) {
return l.first.string < r.first.string;
return CompletionModel::compareStrings(
l.first.string, r.first.string);
});
for (const auto &emote : vec)
{
@@ -111,6 +114,8 @@ namespace {
EmotePopup::EmotePopup(QWidget *parent)
: BasePopup(BaseWindow::EnableCustomFrame, parent)
{
this->moveTo(this, getApp()->windows->emotePopupPos(), false);
auto layout = new QVBoxLayout(this);
this->getLayoutContainer()->setLayout(layout);
@@ -218,4 +223,9 @@ void EmotePopup::loadEmojis()
this->viewEmojis_->setChannel(emojiChannel);
}
void EmotePopup::closeEvent(QCloseEvent *event)
{
getApp()->windows->setEmotePopupPos(this->pos());
QWidget::closeEvent(event);
}
} // namespace chatterino
+2
View File
@@ -19,6 +19,8 @@ public:
void loadChannel(ChannelPtr channel);
void loadEmojis();
virtual void closeEvent(QCloseEvent *event) override;
pajlada::Signals::Signal<Link> linkClicked;
private:
-21
View File
@@ -4,7 +4,6 @@
#include "common/Common.hpp"
#include "common/NetworkRequest.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "providers/twitch/PartialTwitchUser.hpp"
#include "util/Helpers.hpp"
#ifdef USEWINSDK
@@ -159,7 +158,6 @@ AdvancedLoginWidget::AdvancedLoginWidget()
this->ui_.layout.addWidget(&this->ui_.instructionsLabel);
this->ui_.layout.addLayout(&this->ui_.formLayout);
this->ui_.layout.addLayout(&this->ui_.buttonUpperRow.layout);
this->ui_.layout.addLayout(&this->ui_.buttonLowerRow.layout);
this->refreshButtons();
@@ -207,29 +205,10 @@ AdvancedLoginWidget::AdvancedLoginWidget()
LogInWithCredentials(userID, username, clientID, oauthToken);
});
/// Lower button row
this->ui_.buttonLowerRow.fillInUserIDButton.setText(
"Get user ID from username");
this->ui_.buttonLowerRow.layout.addWidget(
&this->ui_.buttonLowerRow.fillInUserIDButton);
connect(&this->ui_.buttonLowerRow.fillInUserIDButton, &QPushButton::clicked,
[=]() {
const auto onIdFetched = [=](const QString &userID) {
this->ui_.userIDInput.setText(userID); //
};
PartialTwitchUser::byName(this->ui_.usernameInput.text())
.getId(onIdFetched, this);
});
}
void AdvancedLoginWidget::refreshButtons()
{
this->ui_.buttonLowerRow.fillInUserIDButton.setEnabled(
!this->ui_.usernameInput.text().isEmpty());
if (this->ui_.userIDInput.text().isEmpty() ||
this->ui_.usernameInput.text().isEmpty() ||
this->ui_.clientIDInput.text().isEmpty() ||
-6
View File
@@ -57,12 +57,6 @@ public:
QPushButton addUserButton;
QPushButton clearFieldsButton;
} buttonUpperRow;
struct {
QHBoxLayout layout;
QPushButton fillInUserIDButton;
} buttonLowerRow;
} ui_;
};
-1
View File
@@ -4,7 +4,6 @@
#include "common/Channel.hpp"
#include "common/NetworkRequest.hpp"
#include "messages/Message.hpp"
#include "providers/twitch/PartialTwitchUser.hpp"
#include "providers/twitch/TwitchChannel.hpp"
#include "providers/twitch/TwitchMessageBuilder.hpp"
#include "util/PostToThread.hpp"
+6 -7
View File
@@ -152,7 +152,7 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent)
auto editor = new IrcConnectionEditor(unique);
if (editor->exec() == QDialog::Accepted)
{
Irc::instance().connections.appendItem(editor->data());
Irc::instance().connections.append(editor->data());
}
});
@@ -160,21 +160,20 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent)
view->getTableView(), &QTableView::doubleClicked,
[](const QModelIndex &index) {
auto editor = new IrcConnectionEditor(
Irc::instance()
.connections.getVector()[size_t(index.row())]);
Irc::instance().connections.raw()[size_t(index.row())]);
if (editor->exec() == QDialog::Accepted)
{
auto data = editor->data();
auto &&conns = Irc::instance().connections.getVector();
auto &&conns = Irc::instance().connections.raw();
int i = 0;
for (auto &&conn : conns)
{
if (conn.id == data.id)
{
Irc::instance().connections.removeItem(
Irc::instance().connections.removeAt(
i, Irc::noEraseCredentialCaller);
Irc::instance().connections.insertItem(data, i);
Irc::instance().connections.insert(data, i);
}
i++;
}
@@ -348,7 +347,7 @@ IndirectChannel SelectChannelDialog::getSelectedChannel() const
->currentIndex()
.row();
auto &&vector = Irc::instance().connections.getVector();
auto &&vector = Irc::instance().connections.raw();
if (row >= 0 && row < int(vector.size()))
{
+73 -57
View File
@@ -4,7 +4,6 @@
#include "singletons/Resources.hpp"
#include "util/LayoutCreator.hpp"
#include "widgets/helper/Button.hpp"
#include "widgets/helper/SettingsDialogTab.hpp"
#include "widgets/settingspages/AboutPage.hpp"
#include "widgets/settingspages/AccountsPage.hpp"
#include "widgets/settingspages/CommandPage.hpp"
@@ -21,22 +20,18 @@
namespace chatterino {
SettingsDialog *SettingsDialog::handle = nullptr;
SettingsDialog::SettingsDialog()
: BaseWindow(BaseWindow::DisableCustomScaling)
{
this->setWindowTitle("Chatterino Settings");
this->resize(815, 600);
this->themeChangedEvent();
this->scaleChangedEvent(this->scale());
this->initUi();
this->addTabs();
this->scaleChangedEvent(this->scale());
this->overrideBackgroundColor_ = QColor("#111111");
this->themeChangedEvent();
this->resize(815, 600);
this->scaleChangedEvent(this->scale()); // execute twice to width of item
}
void SettingsDialog::initUi()
@@ -69,13 +64,9 @@ void SettingsDialog::initUi()
.assign(&this->ui_.tabContainer);
// right side (pages)
auto right =
centerBox.emplace<QVBoxLayout>().withoutMargin().withoutSpacing();
{
right.emplace<QStackedLayout>()
.assign(&this->ui_.pageStack)
.withoutMargin();
}
centerBox.emplace<QStackedLayout>()
.assign(&this->ui_.pageStack)
.withoutMargin();
this->ui_.pageStack->setMargin(0);
@@ -103,10 +94,11 @@ void SettingsDialog::initUi()
void SettingsDialog::filterElements(const QString &text)
{
// filter elements and hide pages
for (auto &&page : this->pages_)
for (auto &&tab : this->tabs_)
{
// filterElements returns true if anything on the page matches the search query
page->tab()->setVisible(page->filterElements(text));
tab->setVisible(tab->page()->filterElements(text) ||
tab->name().contains(text, Qt::CaseInsensitive));
}
// find next visible page
@@ -144,11 +136,6 @@ void SettingsDialog::filterElements(const QString &text)
}
}
SettingsDialog *SettingsDialog::getHandle()
{
return SettingsDialog::handle;
}
void SettingsDialog::addTabs()
{
this->ui_.tabContainer->setMargin(0);
@@ -156,38 +143,34 @@ void SettingsDialog::addTabs()
this->ui_.tabContainer->setContentsMargins(0, 20, 0, 20);
this->addTab(new GeneralPage);
// Constructors are wrapped in std::function to remove some strain from first time loading.
// clang-format off
this->addTab([]{return new GeneralPage;}, "General", ":/settings/about.svg");
this->ui_.tabContainer->addSpacing(16);
this->addTab(new AccountsPage);
this->addTab([]{return new AccountsPage;}, "Accounts", ":/settings/accounts.svg", SettingsTabId::Accounts);
this->ui_.tabContainer->addSpacing(16);
this->addTab(new CommandPage);
this->addTab(new HighlightingPage);
this->addTab(new IgnoresPage);
this->addTab([]{return new CommandPage;}, "Commands", ":/settings/commands.svg");
this->addTab([]{return new HighlightingPage;}, "Highlights", ":/settings/notifications.svg");
this->addTab([]{return new IgnoresPage;}, "Ignores", ":/settings/ignore.svg");
this->ui_.tabContainer->addSpacing(16);
this->addTab(new KeyboardSettingsPage);
this->addTab(this->ui_.moderationPage = new ModerationPage);
this->addTab(new NotificationPage);
this->addTab(new ExternalToolsPage);
this->addTab([]{return new KeyboardSettingsPage;}, "Keybindings", ":/settings/keybinds.svg");
this->addTab([]{return new ModerationPage;}, "Moderation", ":/settings/moderation.svg", SettingsTabId::Moderation);
this->addTab([]{return new NotificationPage;}, "Notifications", ":/settings/notification2.svg");
this->addTab([]{return new ExternalToolsPage;}, "External tools", ":/settings/externaltools.svg");
this->ui_.tabContainer->addStretch(1);
this->addTab(new AboutPage, Qt::AlignBottom);
this->addTab([]{return new AboutPage;}, "About", ":/settings/about.svg", SettingsTabId(), Qt::AlignBottom);
// clang-format on
}
void SettingsDialog::addTab(SettingsPage *page, Qt::Alignment alignment)
void SettingsDialog::addTab(std::function<SettingsPage *()> page,
const QString &name, const QString &iconPath,
SettingsTabId id, Qt::Alignment alignment)
{
auto tab = new SettingsDialogTab(this, page, page->getIconResource());
page->setTab(tab);
auto tab = new SettingsDialogTab(this, std::move(page), name, iconPath, id);
this->ui_.pageStack->addWidget(page);
this->ui_.tabContainer->addWidget(tab, 0, alignment);
this->tabs_.push_back(tab);
this->pages_.push_back(page);
if (this->tabs_.size() == 1)
{
@@ -197,7 +180,16 @@ void SettingsDialog::addTab(SettingsPage *page, Qt::Alignment alignment)
void SettingsDialog::selectTab(SettingsDialogTab *tab, bool byUser)
{
this->ui_.pageStack->setCurrentWidget(tab->getSettingsPage());
// add page if it's not been added yet
[&] {
for (int i = 0; i < this->ui_.pageStack->count(); i++)
if (this->ui_.pageStack->itemAt(i)->widget() == tab->page())
return;
this->ui_.pageStack->addWidget(tab->page());
}();
this->ui_.pageStack->setCurrentWidget(tab->page());
if (this->selectedTab_ != nullptr)
{
@@ -215,28 +207,49 @@ void SettingsDialog::selectTab(SettingsDialogTab *tab, bool byUser)
}
}
void SettingsDialog::selectPage(SettingsPage *page)
void SettingsDialog::selectTab(SettingsTabId id)
{
assert(page);
assert(page->tab());
auto t = this->tab(id);
assert(t);
if (!t)
return;
this->selectTab(page->tab());
this->selectTab(t);
}
SettingsDialogTab *SettingsDialog::tab(SettingsTabId id)
{
for (auto &&tab : this->tabs_)
if (tab->id() == id)
return tab;
assert(false);
return nullptr;
}
void SettingsDialog::showDialog(SettingsDialogPreference preferredTab)
{
static SettingsDialog *instance = new SettingsDialog();
instance->refresh();
static bool hasShownBefore = false;
if (hasShownBefore)
instance->refresh();
hasShownBefore = true;
switch (preferredTab)
{
case SettingsDialogPreference::Accounts:
instance->selectTab(instance->tabs_.at(1));
instance->selectTab(SettingsTabId::Accounts);
break;
case SettingsDialogPreference::ModerationActions:
instance->selectPage(instance->ui_.moderationPage);
instance->ui_.moderationPage->selectModerationActions();
if (auto tab = instance->tab(SettingsTabId::Moderation))
{
instance->selectTab(tab);
if (auto page = dynamic_cast<ModerationPage *>(tab->page()))
{
page->selectModerationActions();
}
}
break;
default:;
@@ -250,11 +263,13 @@ void SettingsDialog::showDialog(SettingsDialogPreference preferredTab)
void SettingsDialog::refresh()
{
// Resets the cancel button.
getSettings()->saveSnapshot();
// Updates tabs.
for (auto *tab : this->tabs_)
{
tab->getSettingsPage()->onShow();
tab->page()->onShow();
}
}
@@ -273,7 +288,8 @@ void SettingsDialog::scaleChangedEvent(float newDpi)
this->setStyleSheet(styleSheet);
this->ui_.tabContainerContainer->setFixedWidth(int(150 * newDpi));
if (this->ui_.tabContainerContainer)
this->ui_.tabContainerContainer->setFixedWidth(int(150 * newDpi));
}
void SettingsDialog::themeChangedEvent()
@@ -281,7 +297,7 @@ void SettingsDialog::themeChangedEvent()
BaseWindow::themeChangedEvent();
QPalette palette;
palette.setColor(QPalette::Background, QColor("#111"));
palette.setColor(QPalette::Window, QColor("#111"));
this->setPalette(palette);
}
@@ -301,7 +317,7 @@ void SettingsDialog::onCancelClicked()
{
for (auto &tab : this->tabs_)
{
tab->getSettingsPage()->cancel();
tab->page()->cancel();
}
getSettings()->restoreSnapshot();
+8 -8
View File
@@ -6,7 +6,9 @@
#include <QStackedLayout>
#include <QVBoxLayout>
#include <QWidget>
#include <functional>
#include <pajlada/settings/setting.hpp>
#include "widgets/helper/SettingsDialogTab.hpp"
class QLineEdit;
@@ -32,7 +34,6 @@ class SettingsDialog : public BaseWindow
public:
SettingsDialog();
static SettingsDialog *getHandle(); // may be NULL
static void showDialog(SettingsDialogPreference preferredTab =
SettingsDialogPreference::NoPreference);
@@ -42,15 +43,16 @@ protected:
virtual void showEvent(QShowEvent *) override;
private:
static SettingsDialog *handle;
void refresh();
void initUi();
SettingsDialogTab *tab(SettingsTabId id);
void addTabs();
void addTab(SettingsPage *page, Qt::Alignment alignment = Qt::AlignTop);
void selectTab(SettingsDialogTab *tab, bool byUser = true);
void selectPage(SettingsPage *page);
void addTab(std::function<SettingsPage *()> page, const QString &name,
const QString &iconPath, SettingsTabId id = {},
Qt::Alignment alignment = Qt::AlignTop);
void selectTab(SettingsDialogTab *tab, const bool byUser = true);
void selectTab(SettingsTabId id);
void filterElements(const QString &query);
void onOkClicked();
@@ -62,11 +64,9 @@ private:
QStackedLayout *pageStack{};
QPushButton *okButton{};
QPushButton *cancelButton{};
ModerationPage *moderationPage{};
QLineEdit *search{};
} ui_;
std::vector<SettingsDialogTab *> tabs_;
std::vector<SettingsPage *> pages_;
SettingsDialogTab *selectedTab_{};
SettingsDialogTab *lastSelectedByUser_{};
+74 -63
View File
@@ -4,10 +4,12 @@
#include "common/Channel.hpp"
#include "common/NetworkRequest.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "controllers/highlights/HighlightController.hpp"
#include "providers/twitch/PartialTwitchUser.hpp"
#include "controllers/highlights/HighlightBlacklistUser.hpp"
#include "providers/twitch/TwitchChannel.hpp"
#include "providers/twitch/api/Helix.hpp"
#include "providers/twitch/api/Kraken.hpp"
#include "singletons/Resources.hpp"
#include "singletons/Settings.hpp"
#include "util/LayoutCreator.hpp"
#include "util/PostToThread.hpp"
#include "widgets/Label.hpp"
@@ -21,9 +23,9 @@
#include <QNetworkAccessManager>
#include <QNetworkReply>
#define TEXT_FOLLOWERS "Followers: "
#define TEXT_VIEWS "Views: "
#define TEXT_CREATED "Created: "
const QString TEXT_VIEWS("Views: %1");
const QString TEXT_FOLLOWERS("Followers: %1");
const QString TEXT_CREATED("Created: %1");
#define TEXT_USER_ID "ID: "
#define TEXT_UNAVAILABLE "(not available)"
@@ -91,10 +93,11 @@ UserInfoPopup::UserInfoPopup()
this->ui_.userIDLabel->setPalette(palette);
}
vbox.emplace<Label>(TEXT_VIEWS).assign(&this->ui_.viewCountLabel);
vbox.emplace<Label>(TEXT_FOLLOWERS)
vbox.emplace<Label>(TEXT_VIEWS.arg(""))
.assign(&this->ui_.viewCountLabel);
vbox.emplace<Label>(TEXT_FOLLOWERS.arg(""))
.assign(&this->ui_.followerCountLabel);
vbox.emplace<Label>(TEXT_CREATED)
vbox.emplace<Label>(TEXT_CREATED.arg(""))
.assign(&this->ui_.createdDateLabel);
}
}
@@ -155,8 +158,6 @@ UserInfoPopup::UserInfoPopup()
if (twitchChannel)
{
qDebug() << this->userName_;
bool isMyself =
QString::compare(
getApp()->accounts->twitch.getCurrent()->getUserName(),
@@ -259,10 +260,6 @@ void UserInfoPopup::installEvents()
this->ui_.follow, &QCheckBox::stateChanged, [this](int) mutable {
auto currentUser = getApp()->accounts->twitch.getCurrent();
QUrl requestUrl("https://api.twitch.tv/kraken/users/" +
currentUser->getUserId() + "/follows/channels/" +
this->userId_);
const auto reenableFollowCheckbox = [this] {
this->ui_.follow->setEnabled(true); //
};
@@ -336,24 +333,23 @@ void UserInfoPopup::installEvents()
if (checked)
{
getApp()->highlights->blacklistedUsers.insertItem(
getSettings()->blacklistedUsers.insert(
HighlightBlacklistUser{this->userName_, false});
this->ui_.ignoreHighlights->setEnabled(true);
}
else
{
const auto &vector =
getApp()->highlights->blacklistedUsers.getVector();
const auto &vector = getSettings()->blacklistedUsers.raw();
for (int i = 0; i < vector.size(); i++)
{
if (this->userName_ == vector[i].getPattern())
{
getApp()->highlights->blacklistedUsers.removeItem(i);
getSettings()->blacklistedUsers.removeAt(i);
i--;
}
}
if (getApp()->highlights->blacklistContains(this->userName_))
if (getSettings()->isBlacklistedUser(this->userName_))
{
this->ui_.ignoreHighlights->setToolTip(
"Name matched by regex");
@@ -383,14 +379,17 @@ void UserInfoPopup::updateUserData()
{
std::weak_ptr<bool> hack = this->hack_;
const auto onIdFetchFailed = [this]() {
const auto onUserFetchFailed = [this, hack] {
if (!hack.lock())
{
return;
}
// this can occur when the account doesn't exist.
this->ui_.followerCountLabel->setText(TEXT_FOLLOWERS +
QString(TEXT_UNAVAILABLE));
this->ui_.viewCountLabel->setText(TEXT_VIEWS +
QString(TEXT_UNAVAILABLE));
this->ui_.createdDateLabel->setText(TEXT_CREATED +
QString(TEXT_UNAVAILABLE));
this->ui_.followerCountLabel->setText(
TEXT_FOLLOWERS.arg(TEXT_UNAVAILABLE));
this->ui_.viewCountLabel->setText(TEXT_VIEWS.arg(TEXT_UNAVAILABLE));
this->ui_.createdDateLabel->setText(TEXT_CREATED.arg(TEXT_UNAVAILABLE));
this->ui_.nameLabel->setText(this->userName_);
@@ -399,47 +398,59 @@ void UserInfoPopup::updateUserData()
this->ui_.userIDLabel->setProperty("copy-text",
QString(TEXT_UNAVAILABLE));
};
const auto onIdFetched = [this, hack](QString id) {
const auto onUserFetched = [this, hack](const auto &user) {
if (!hack.lock())
{
return;
}
auto currentUser = getApp()->accounts->twitch.getCurrent();
this->userId_ = id;
this->userId_ = user.id;
this->ui_.userIDLabel->setText(TEXT_USER_ID + id);
this->ui_.userIDLabel->setProperty("copy-text", id);
// don't wait for the request to complete, just put the user id in the card
// right away
this->ui_.userIDLabel->setText(TEXT_USER_ID + user.id);
this->ui_.userIDLabel->setProperty("copy-text", user.id);
QString url("https://api.twitch.tv/kraken/channels/" + id);
NetworkRequest::twitchRequest(url)
.caller(this)
.onSuccess([this](auto result) -> Outcome {
auto obj = result.parseJson();
this->ui_.followerCountLabel->setText(
TEXT_FOLLOWERS +
QString::number(obj.value("followers").toInt()));
this->ui_.viewCountLabel->setText(
TEXT_VIEWS + QString::number(obj.value("views").toInt()));
this->ui_.viewCountLabel->setText(TEXT_VIEWS.arg(user.viewCount));
getKraken()->getUser(
user.id,
[this, hack](const auto &user) {
if (!hack.lock())
{
return;
}
this->ui_.createdDateLabel->setText(
TEXT_CREATED +
obj.value("created_at").toString().section("T", 0, 0));
TEXT_CREATED.arg(user.createdAt.section("T", 0, 0)));
},
[] {
// failure
});
this->loadAvatar(user.profileImageUrl);
this->loadAvatar(QUrl(obj.value("logo").toString()));
return Success;
})
.execute();
getHelix()->getUserFollowers(
user.id,
[this, hack](const auto &followers) {
if (!hack.lock())
{
return;
}
this->ui_.followerCountLabel->setText(
TEXT_FOLLOWERS.arg(followers.total));
},
[] {
// on failure
});
// get follow state
currentUser->checkFollow(id, [this, hack](auto result) {
if (hack.lock())
currentUser->checkFollow(user.id, [this, hack](auto result) {
if (!hack.lock())
{
if (result != FollowResult_Failed)
{
this->ui_.follow->setEnabled(true);
this->ui_.follow->setChecked(result ==
FollowResult_Following);
}
return;
}
if (result != FollowResult_Failed)
{
this->ui_.follow->setEnabled(true);
this->ui_.follow->setChecked(result == FollowResult_Following);
}
});
@@ -447,7 +458,7 @@ void UserInfoPopup::updateUserData()
bool isIgnoring = false;
for (const auto &ignoredUser : currentUser->getIgnores())
{
if (id == ignoredUser.id)
if (user.id == ignoredUser.id)
{
isIgnoring = true;
break;
@@ -456,7 +467,7 @@ void UserInfoPopup::updateUserData()
// get ignoreHighlights state
bool isIgnoringHighlights = false;
const auto &vector = getApp()->highlights->blacklistedUsers.getVector();
const auto &vector = getSettings()->blacklistedUsers.raw();
for (int i = 0; i < vector.size(); i++)
{
if (this->userName_ == vector[i].getPattern())
@@ -465,7 +476,7 @@ void UserInfoPopup::updateUserData()
break;
}
}
if (getApp()->highlights->blacklistContains(this->userName_) &&
if (getSettings()->isBlacklistedUser(this->userName_) &&
!isIgnoringHighlights)
{
this->ui_.ignoreHighlights->setToolTip("Name matched by regex");
@@ -479,8 +490,8 @@ void UserInfoPopup::updateUserData()
this->ui_.ignoreHighlights->setChecked(isIgnoringHighlights);
};
PartialTwitchUser::byName(this->userName_)
.getId(onIdFetched, onIdFetchFailed, this);
getHelix()->getUserByName(this->userName_, onUserFetched,
onUserFetchFailed);
this->ui_.follow->setEnabled(false);
this->ui_.ignore->setEnabled(false);
-1
View File
@@ -154,7 +154,6 @@ void Button::fancyPaint(QPainter &painter)
return;
}
painter.setRenderHint(QPainter::HighQualityAntialiasing);
painter.setRenderHint(QPainter::Antialiasing);
QColor c;
+101 -8
View File
@@ -6,6 +6,7 @@
#include <QGraphicsBlurEffect>
#include <QMessageBox>
#include <QPainter>
#include <QScreen>
#include <algorithm>
#include <chrono>
#include <cmath>
@@ -15,6 +16,7 @@
#include "Application.hpp"
#include "common/Common.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "controllers/commands/CommandController.hpp"
#include "debug/Benchmark.hpp"
#include "messages/Emote.hpp"
#include "messages/LimitedQueueSnapshot.hpp"
@@ -24,6 +26,7 @@
#include "messages/layouts/MessageLayoutElement.hpp"
#include "providers/twitch/TwitchChannel.hpp"
#include "providers/twitch/TwitchIrcServer.hpp"
#include "singletons/Resources.hpp"
#include "singletons/Settings.hpp"
#include "singletons/Theme.hpp"
#include "singletons/TooltipPreviewImage.hpp"
@@ -113,6 +116,10 @@ ChannelView::ChannelView(BaseWidget *parent)
this->initializeScrollbar();
this->initializeSignals();
this->cursors_.neutral = QCursor(getResources().scrolling.neutralScroll);
this->cursors_.up = QCursor(getResources().scrolling.upScroll);
this->cursors_.down = QCursor(getResources().scrolling.downScroll);
this->pauseTimer_.setSingleShot(true);
QObject::connect(&this->pauseTimer_, &QTimer::timeout, this, [this] {
/// remove elements that are finite
@@ -130,6 +137,10 @@ ChannelView::ChannelView(BaseWidget *parent)
this->clickTimer_->setSingleShot(true);
this->clickTimer_->setInterval(500);
this->scrollTimer_.setInterval(20);
QObject::connect(&this->scrollTimer_, &QTimer::timeout, this,
&ChannelView::scrollUpdateRequested);
this->setFocusPolicy(Qt::FocusPolicy::StrongFocus);
}
@@ -1059,8 +1070,13 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event)
return;
}
if (this->isScrolling_)
{
this->currentMousePosition_ = event->screenPos();
}
// is selecting
if (this->isMouseDown_)
if (this->isLeftMouseDown_)
{
// this->pause(PauseReason::Selecting, 300);
int index = layout->getSelectionIndex(relativePos);
@@ -1325,8 +1341,11 @@ void ChannelView::mousePressEvent(QMouseEvent *event)
switch (event->button())
{
case Qt::LeftButton: {
this->lastPressPosition_ = event->screenPos();
this->isMouseDown_ = true;
if (this->isScrolling_)
this->disableScrolling();
this->lastLeftPressPosition_ = event->screenPos();
this->isLeftMouseDown_ = true;
if (layout->flags.has(MessageLayoutFlag::Collapsed))
return;
@@ -1343,11 +1362,22 @@ void ChannelView::mousePressEvent(QMouseEvent *event)
break;
case Qt::RightButton: {
if (this->isScrolling_)
this->disableScrolling();
this->lastRightPressPosition_ = event->screenPos();
this->isRightMouseDown_ = true;
}
break;
case Qt::MiddleButton: {
if (this->isScrolling_)
this->disableScrolling();
else
this->enableScrolling(event->screenPos());
}
break;
default:;
}
@@ -1372,11 +1402,11 @@ void ChannelView::mouseReleaseEvent(QMouseEvent *event)
return;
}
}
else if (this->isMouseDown_)
else if (this->isLeftMouseDown_)
{
this->isMouseDown_ = false;
this->isLeftMouseDown_ = false;
if (fabsf(distanceBetweenPoints(this->lastPressPosition_,
if (fabsf(distanceBetweenPoints(this->lastLeftPressPosition_,
event->screenPos())) > 15.f)
{
return;
@@ -1404,6 +1434,13 @@ void ChannelView::mouseReleaseEvent(QMouseEvent *event)
return;
}
}
else if (event->button() == Qt::MiddleButton)
{
if (event->screenPos() == this->lastMiddlePressPosition_)
this->enableScrolling(event->screenPos());
else
this->disableScrolling();
}
else
{
// not left or right button
@@ -1637,7 +1674,7 @@ void ChannelView::mouseDoubleClickEvent(QMouseEvent *event)
return;
}
if (!this->isMouseDown_)
if (!this->isLeftMouseDown_)
{
this->isDoubleClick_ = true;
@@ -1701,7 +1738,6 @@ void ChannelView::handleLinkClick(QMouseEvent *event, const Link &link,
case Link::UserInfo: {
auto user = link.value;
this->showUserInfoPopup(user);
qDebug() << "Clicked " << user << "s message";
}
break;
@@ -1721,6 +1757,8 @@ void ChannelView::handleLinkClick(QMouseEvent *event, const Link &link,
.replace("{msg-id}", layout->getMessage()->id)
.replace("{message}", layout->getMessage()->messageText);
value =
getApp()->commands->execCommand(value, this->channel_, false);
this->channel_->sendMessage(value);
}
break;
@@ -1801,4 +1839,59 @@ void ChannelView::getWordBounds(MessageLayout *layout,
wordEnd = wordStart + length;
}
void ChannelView::enableScrolling(const QPointF &scrollStart)
{
this->isScrolling_ = true;
this->lastMiddlePressPosition_ = scrollStart;
// The line below prevents a sudden jerk at the beginning
this->currentMousePosition_ = scrollStart;
this->scrollTimer_.start();
if (!QGuiApplication::overrideCursor())
QGuiApplication::setOverrideCursor(this->cursors_.neutral);
}
void ChannelView::disableScrolling()
{
this->isScrolling_ = false;
this->scrollTimer_.stop();
QGuiApplication::restoreOverrideCursor();
}
void ChannelView::scrollUpdateRequested()
{
const qreal dpi =
QGuiApplication::screenAt(this->pos())->devicePixelRatio();
const qreal delta = dpi * (this->currentMousePosition_.y() -
this->lastMiddlePressPosition_.y());
const int cursorHeight = this->cursors_.neutral.pixmap().height();
if (fabs(delta) <= cursorHeight * dpi)
{
/*
* If within an area close to the initial position, don't do any
* scrolling at all.
*/
QGuiApplication::changeOverrideCursor(this->cursors_.neutral);
return;
}
qreal offset;
if (delta > 0)
{
QGuiApplication::changeOverrideCursor(this->cursors_.down);
offset = delta - cursorHeight;
}
else
{
QGuiApplication::changeOverrideCursor(this->cursors_.up);
offset = delta + cursorHeight;
}
// "Good" feeling multiplier found by trial-and-error
const qreal multiplier = qreal(0.02);
this->scrollBar_->offset(multiplier * offset);
}
} // namespace chatterino
+18 -9
View File
@@ -148,6 +148,9 @@ private:
void updatePauses();
void unpaused();
void enableScrolling(const QPointF &scrollStart);
void disableScrolling();
QTimer *layoutCooldown_;
bool layoutQueued_;
@@ -183,27 +186,31 @@ private:
bool onlyUpdateEmotes_ = false;
// Mouse event variables
bool isMouseDown_ = false;
bool isLeftMouseDown_ = false;
bool isRightMouseDown_ = false;
bool isDoubleClick_ = false;
DoubleClickSelection doubleClickSelection_;
QPointF lastPressPosition_;
QPointF lastLeftPressPosition_;
QPointF lastRightPressPosition_;
QPointF lastDClickPosition_;
QTimer *clickTimer_;
bool isScrolling_ = false;
QPointF lastMiddlePressPosition_;
QPointF currentMousePosition_;
QTimer scrollTimer_;
struct {
QCursor neutral;
QCursor up;
QCursor down;
} cursors_;
Selection selection_;
bool selecting_ = false;
LimitedQueue<MessageLayoutPtr> messages_;
pajlada::Signals::Connection messageAppendedConnection_;
pajlada::Signals::Connection messageAddedAtStartConnection_;
pajlada::Signals::Connection messageRemovedConnection_;
pajlada::Signals::Connection messageReplacedConnection_;
pajlada::Signals::Connection repaintGifsConnection_;
pajlada::Signals::Connection layoutConnection_;
std::vector<pajlada::Signals::ScopedConnection> connections_;
std::vector<pajlada::Signals::ScopedConnection> channelConnections_;
@@ -218,6 +225,8 @@ private slots:
queueLayout();
update();
}
void scrollUpdateRequested();
};
} // namespace chatterino
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once
#define OPEN_IN_BROWSER "Open in browser"
#define OPEN_PLAYER_IN_BROWSER "Open in player in browser"
#define OPEN_PLAYER_IN_BROWSER "Open player in browser"
#define OPEN_IN_STREAMLINK "Open in streamlink"
#define DONT_OPEN "Don't open"
+16 -8
View File
@@ -15,9 +15,12 @@ EditableModelView::EditableModelView(QAbstractTableModel *model)
{
this->model_->setParent(this);
this->tableView_->setModel(model);
this->tableView_->setSelectionMode(QAbstractItemView::ExtendedSelection);
this->tableView_->setSelectionMode(QAbstractItemView::SingleSelection);
this->tableView_->setSelectionBehavior(QAbstractItemView::SelectRows);
this->tableView_->verticalHeader()->hide();
this->tableView_->setDragDropMode(QTableView::DragDropMode::InternalMove);
this->tableView_->setDragDropOverwriteMode(false);
this->tableView_->setDefaultDropAction(Qt::DropAction::MoveAction);
this->tableView_->verticalHeader()->setVisible(false);
// create layout
QVBoxLayout *vbox = new QVBoxLayout(this);
@@ -38,12 +41,17 @@ EditableModelView::EditableModelView(QAbstractTableModel *model)
QPushButton *remove = new QPushButton("Remove");
buttons->addWidget(remove);
QObject::connect(remove, &QPushButton::clicked, [this] {
QModelIndexList list;
while ((list = this->getTableView()->selectionModel()->selectedRows(0))
.length() > 0)
{
model_->removeRow(list[0].row());
}
auto selected = this->getTableView()->selectionModel()->selectedRows(0);
// Remove rows backwards so indices don't shift.
std::vector<int> rows;
for (auto &&index : selected)
rows.push_back(index.row());
std::sort(rows.begin(), rows.end(), std::greater{});
for (auto &&row : rows)
model_->removeRow(row);
});
buttons->addStretch();
+9 -11
View File
@@ -91,7 +91,6 @@ void NotebookButton::paintEvent(QPaintEvent *event)
case User: {
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::HighQualityAntialiasing);
auto a = w / 8;
QPainterPath path;
@@ -99,19 +98,18 @@ void NotebookButton::paintEvent(QPaintEvent *event)
path.arcMoveTo(a, 4 * a, 6 * a, 6 * a, 0);
path.arcTo(a, 4 * a, 6 * a, 6 * a, 0, 180);
QPainterPath remove;
remove.addEllipse(2 * a, 1 * a, 4 * a, 4 * a);
path = path.subtracted(remove);
path.addEllipse(2.5 * a, 1.5 * a, 3 * a, 3 * a);
painter.fillPath(path, foreground);
painter.setBrush(background);
painter.drawEllipse(2 * a, 1 * a, 4 * a, 4 * a);
painter.setBrush(foreground);
painter.drawEllipse(2.5 * a, 1.5 * a, 3 * a + 1, 3 * a);
}
break;
case Settings: {
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::HighQualityAntialiasing);
auto a = w / 8;
QPainterPath path;
@@ -126,10 +124,10 @@ void NotebookButton::paintEvent(QPaintEvent *event)
i * (360 / 8.0) + (360 / 32.0), (360 / 32.0));
}
painter.fillPath(path, foreground);
QPainterPath remove;
remove.addEllipse(3 * a, 3 * a, 2 * a, 2 * a);
painter.setBrush(background);
painter.drawEllipse(3 * a, 3 * a, 2 * a, 2 * a);
painter.fillPath(path.subtracted(remove), foreground);
}
break;
+7 -1
View File
@@ -13,9 +13,10 @@ ScrollbarHighlight::ScrollbarHighlight()
}
ScrollbarHighlight::ScrollbarHighlight(const std::shared_ptr<QColor> color,
Style style)
Style style, bool isRedeemedHighlight)
: color_(color)
, style_(style)
, isRedeemedHighlight_(isRedeemedHighlight)
{
}
@@ -29,6 +30,11 @@ ScrollbarHighlight::Style ScrollbarHighlight::getStyle() const
return this->style_;
}
bool ScrollbarHighlight::isRedeemedHighlight() const
{
return this->isRedeemedHighlight_;
}
bool ScrollbarHighlight::isNull() const
{
return this->style_ == None;
+3 -1
View File
@@ -17,15 +17,17 @@ public:
ScrollbarHighlight();
ScrollbarHighlight(const std::shared_ptr<QColor> color,
Style style = Default);
Style style = Default, bool isRedeemedHighlight = false);
QColor getColor() const;
Style getStyle() const;
bool isRedeemedHighlight() const;
bool isNull() const;
private:
std::shared_ptr<QColor> color_;
Style style_;
bool isRedeemedHighlight_;
};
} // namespace chatterino
+23 -4
View File
@@ -8,12 +8,16 @@
namespace chatterino {
SettingsDialogTab::SettingsDialogTab(SettingsDialog *_dialog,
SettingsPage *_page, QString imageFileName)
std::function<SettingsPage *()> _lazyPage,
const QString &name, QString imageFileName,
SettingsTabId id)
: BaseWidget(_dialog)
, dialog_(_dialog)
, page_(_page)
, lazyPage_(std::move(_lazyPage))
, id_(id)
, name_(name)
{
this->ui_.labelText = page_->getName();
this->ui_.labelText = name;
this->ui_.icon.addFile(imageFileName);
this->setCursor(QCursor(Qt::PointingHandCursor));
@@ -34,8 +38,13 @@ void SettingsDialogTab::setSelected(bool _selected)
emit selectedChanged(selected_);
}
SettingsPage *SettingsDialogTab::getSettingsPage()
SettingsPage *SettingsDialogTab::page()
{
if (this->page_)
return this->page_;
this->page_ = this->lazyPage_();
this->page_->setTab(this);
return this->page_;
}
@@ -72,4 +81,14 @@ void SettingsDialogTab::mousePressEvent(QMouseEvent *event)
this->setFocus();
}
const QString &SettingsDialogTab::name() const
{
return name_;
}
SettingsTabId SettingsDialogTab::id() const
{
return id_;
}
} // namespace chatterino
+20 -5
View File
@@ -5,22 +5,34 @@
#include <QIcon>
#include <QPaintEvent>
#include <QWidget>
#include <functional>
namespace chatterino {
class SettingsPage;
class SettingsDialog;
enum SettingsTabId {
None,
Accounts,
Moderation,
};
class SettingsDialogTab : public BaseWidget
{
Q_OBJECT
public:
SettingsDialogTab(SettingsDialog *dialog_, SettingsPage *page_,
QString imageFileName);
SettingsDialogTab(SettingsDialog *dialog_,
std::function<SettingsPage *()> page_,
const QString &name, QString imageFileName,
SettingsTabId id);
void setSelected(bool selected_);
SettingsPage *getSettingsPage();
SettingsPage *page();
SettingsTabId id() const;
const QString &name() const;
signals:
void selectedChanged(bool);
@@ -35,8 +47,11 @@ private:
} ui_;
// Parent settings dialog
SettingsDialog *dialog_;
SettingsPage *page_;
SettingsDialog *dialog_{};
SettingsPage *page_{};
std::function<SettingsPage *()> lazyPage_;
SettingsTabId id_;
QString name_;
bool selected_ = false;
};
-2
View File
@@ -67,7 +67,6 @@ void TitleBarButton::paintEvent(QPaintEvent *event)
color = "#999";
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::HighQualityAntialiasing);
auto a = xD / 3;
QPainterPath path;
@@ -92,7 +91,6 @@ void TitleBarButton::paintEvent(QPaintEvent *event)
case TitleBarButtonStyle::Settings: {
color = "#999";
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::HighQualityAntialiasing);
painter.save();
painter.translate(3, 3);
-1
View File
@@ -18,7 +18,6 @@
namespace chatterino {
AboutPage::AboutPage()
: SettingsPage("About", ":/settings/about.svg")
{
LayoutCreator<AboutPage> layoutCreator(this);
@@ -17,7 +17,6 @@
namespace chatterino {
AccountsPage::AccountsPage()
: SettingsPage("Accounts", ":/settings/accounts.svg")
{
auto *app = getApp();
+3 -3
View File
@@ -33,7 +33,6 @@ namespace {
} // namespace
CommandPage::CommandPage()
: SettingsPage("Commands", ":/settings/commands.svg")
{
auto app = getApp();
@@ -47,10 +46,11 @@ CommandPage::CommandPage()
view->setTitles({"Trigger", "Command"});
view->getTableView()->horizontalHeader()->setStretchLastSection(true);
view->addButtonPressed.connect([] {
getApp()->commands->items_.appendItem(
getApp()->commands->items_.append(
Command{"/command", "I made a new command HeyGuys"});
});
// TODO: asyncronously check path
if (QFile(c1settingsPath()).exists())
{
auto button = new QPushButton("Import commands from Chatterino 1");
@@ -65,7 +65,7 @@ CommandPage::CommandPage()
{
if (int index = line.indexOf(' '); index != -1)
{
getApp()->commands->items_.insertItem(
getApp()->commands->items_.insert(
Command(line.mid(0, index), line.mid(index + 1)));
}
}
@@ -12,7 +12,6 @@
namespace chatterino {
ExternalToolsPage::ExternalToolsPage()
: SettingsPage("External tools", ":/settings/externaltools.svg")
{
LayoutCreator<ExternalToolsPage> layoutCreator(this);
auto layout = layoutCreator.setLayoutType<QVBoxLayout>();
@@ -69,6 +68,23 @@ ExternalToolsPage::ExternalToolsPage()
this->managedConnections_);
}
{
auto group = layout.emplace<QGroupBox>("Custom URI Scheme");
auto groupLayout = group.setLayoutType<QFormLayout>();
const auto description = new QLabel(
"You can open video streams directly in any video player that "
"has built-in Twitch support and has own URI Scheme.\nE.g.: "
"IINA for macOS and Potplayer (with extension) for Windows.");
description->setWordWrap(true);
description->setStyleSheet("color: #bbb");
groupLayout->setWidget(0, QFormLayout::SpanningRole, description);
groupLayout->addRow("URI Scheme:", this->createLineEdit(
getSettings()->customURIScheme));
}
layout->addStretch(1);
}
+51 -43
View File
@@ -74,14 +74,16 @@ namespace {
TitleLabel *SettingsLayout::addTitle(const QString &title)
{
auto label = new TitleLabel(title + ":");
// space
if (!this->groups_.empty())
this->addWidget(this->groups_.back().space = new Space);
if (this->count() == 0)
label->setStyleSheet("margin-top: 0");
// title
auto label = new TitleLabel(title + ":");
this->addWidget(label);
// groups
this->groups_.push_back(Group{title, label, {}});
this->groups_.push_back(Group{title, label, nullptr, {}});
return label;
}
@@ -228,6 +230,8 @@ bool SettingsLayout::filterElements(const QString &query)
}
}
if (group.space)
group.space->setVisible(groupAny);
group.title->setVisible(groupAny);
any |= groupAny;
}
@@ -237,7 +241,6 @@ bool SettingsLayout::filterElements(const QString &query)
}
GeneralPage::GeneralPage()
: SettingsPage("General", ":/settings/about.svg")
{
auto y = new QVBoxLayout;
auto scroll = new QScrollArea;
@@ -263,9 +266,7 @@ GeneralPage::GeneralPage()
bool GeneralPage::filterElements(const QString &query)
{
if (this->settingsLayout_)
return this->settingsLayout_->filterElements(query) ||
this->name_.contains(query, Qt::CaseInsensitive) ||
query.isEmpty();
return this->settingsLayout_->filterElements(query) || query.isEmpty();
else
return false;
}
@@ -304,6 +305,12 @@ void GeneralPage::initLayout(SettingsLayout &layout)
layout.addCheckbox("Start with Windows", s.autorun);
#endif
layout.addCheckbox("Restart on crash", s.restartOnCrash);
if (!BaseWindow::supportsCustomWindowFrame())
{
layout.addCheckbox("Show preferences button (ctrl+p to show)",
s.hidePreferencesButton, true);
layout.addCheckbox("Show user button", s.hideUserButton, true);
}
layout.addTitle("Chat");
@@ -321,7 +328,8 @@ void GeneralPage::initLayout(SettingsLayout &layout)
layout.addCheckbox("Smooth scrolling on new messages",
s.enableSmoothScrollingNewMessages);
layout.addDropdown<float>(
"Pause on hover", {"Disabled", "0.5s", "1s", "2s", "5s", "Indefinite"},
"Pause after hover",
{"Disabled", "0.5s", "1s", "2s", "5s", "Indefinite"},
s.pauseOnHoverDuration,
[](auto val) {
if (val < -0.5f)
@@ -344,12 +352,6 @@ void GeneralPage::initLayout(SettingsLayout &layout)
s.pauseChatModifier);
layout.addCheckbox("Show input when it's empty", s.showEmptyInput);
layout.addCheckbox("Show message length while typing", s.showMessageLength);
if (!BaseWindow::supportsCustomWindowFrame())
{
layout.addCheckbox("Show preferences button (ctrl+p to show)",
s.hidePreferencesButton, true);
layout.addCheckbox("Show user button", s.hideUserButton, true);
}
layout.addTitle("Messages");
layout.addCheckbox("Seperate with lines", s.separateMessages);
@@ -358,7 +360,9 @@ void GeneralPage::initLayout(SettingsLayout &layout)
// layout.addDropdown("Last read message style", {"Default"});
layout.addCheckbox("Show deleted messages", s.hideModerated, true);
layout.addDropdown<QString>(
"Timestamps", {"Disable", "h:mm", "hh:mm", "h:mm a", "hh:mm a"},
"Timestamps",
{"Disable", "h:mm", "hh:mm", "h:mm a", "hh:mm a", "h:mm:ss", "hh:mm:ss",
"h:mm:ss a", "hh:mm:ss a"},
s.timestampFormat,
[](auto val) {
return getSettings()->showTimestamps.getValue()
@@ -412,6 +416,32 @@ void GeneralPage::initLayout(SettingsLayout &layout)
"Apple", "Google", "Messenger"},
s.emojiSet);
layout.addTitle("R9K");
layout.addDescription(
"Hide similar messages by the same user. Temporarily show hidden "
"messages by pressing Ctrl+H.");
layout.addCheckbox("Hide similar messages", s.similarityEnabled);
//layout.addCheckbox("Gray out matches", s.colorSimilarDisabled);
layout.addCheckbox("Hide my own messages", s.hideSimilarMyself);
layout.addCheckbox("Receive notification sounds from hidden messages",
s.shownSimilarTriggerHighlights);
s.hideSimilar.connect(
[]() { getApp()->windows->forceLayoutChannelViews(); }, false);
layout.addDropdown<float>(
"Similarity threshold", {"0.5", "0.75", "0.9"}, s.similarityPercentage,
[](auto val) { return QString::number(val); },
[](auto args) { return fuzzyToFloat(args.value, 0.9f); });
layout.addDropdown<int>(
"Maximum delay between messages",
{"5s", "10s", "15s", "30s", "60s", "120s"}, s.hideSimilarMaxDelay,
[](auto val) { return QString::number(val) + "s"; },
[](auto args) { return fuzzyToInt(args.value, 5); });
layout.addDropdown<int>(
"Amount of previous messages to check", {"1", "2", "3", "4", "5"},
s.hideSimilarMaxMessagesToCheck,
[](auto val) { return QString::number(val); },
[](auto args) { return fuzzyToInt(args.value, 3); });
layout.addTitle("Visible badges");
layout.addCheckbox("Authority (staff, admin)",
getSettings()->showBadgesGlobalAuthority);
@@ -497,6 +527,8 @@ void GeneralPage::initLayout(SettingsLayout &layout)
layout.addCheckbox(
"Only search for emote autocompletion at the start of emote names",
s.prefixOnlyEmoteCompletion);
layout.addCheckbox("Only search for username autocompletion with an @",
s.userCompletionOnlyWithAt);
layout.addCheckbox("Show twitch whispers inline", s.inlineWhispers);
layout.addCheckbox("Highlight received inline whispers",
@@ -508,6 +540,9 @@ void GeneralPage::initLayout(SettingsLayout &layout)
s.enableExperimentalIrc);
layout.addCheckbox("Show unhandled IRC messages",
s.showUnhandledIrcMessages);
layout.addCheckbox(
"Hide viewercount and stream length while hovering the split",
s.hideViewerCountAndDuration);
layout.addTitle("Cache");
layout.addDescription(
@@ -547,33 +582,6 @@ void GeneralPage::initLayout(SettingsLayout &layout)
QDesktopServices::openUrl(getPaths()->rootAppDataDirectory);
});
layout.addTitle("Similarity");
layout.addDescription(
"Hides or grays out similar messages from the same user");
layout.addCheckbox("Similarity enabled", s.similarityEnabled);
layout.addCheckbox("Gray out similar messages", s.colorSimilarDisabled);
layout.addCheckbox("Hide similar messages (Ctrl + H)", s.hideSimilar);
layout.addCheckbox("Hide or gray out my own similar messages",
s.hideSimilarMyself);
layout.addCheckbox("Shown similar messages trigger highlights",
s.shownSimilarTriggerHighlights);
layout.addDropdown<float>(
"Similarity percentage", {"0.5", "0.75", "0.9"}, s.similarityPercentage,
[](auto val) { return QString::number(val); },
[](auto args) { return fuzzyToFloat(args.value, 0.9f); });
s.hideSimilar.connect(
[]() { getApp()->windows->forceLayoutChannelViews(); }, false);
layout.addDropdown<int>(
"Similar messages max delay in seconds",
{"5", "10", "15", "30", "60", "120"}, s.hideSimilarMaxDelay,
[](auto val) { return QString::number(val); },
[](auto args) { return fuzzyToInt(args.value, 5); });
layout.addDropdown<int>(
"Similar messages max previous messages to check",
{"1", "2", "3", "4", "5"}, s.hideSimilarMaxMessagesToCheck,
[](auto val) { return QString::number(val); },
[](auto args) { return fuzzyToInt(args.value, 3); });
// invisible element for width
auto inv = new BaseWidget(this);
inv->setScaleIndependantWidth(500);
@@ -17,6 +17,11 @@ class QComboBox;
namespace chatterino {
class Space : public QLabel
{
Q_OBJECT
};
class TitleLabel : public QLabel
{
Q_OBJECT
@@ -162,6 +167,7 @@ private:
struct Group {
QString name;
QWidget *title{};
Space *space{};
std::vector<Widget> widgets;
};
+27 -26
View File
@@ -2,7 +2,6 @@
#include "Application.hpp"
#include "controllers/highlights/HighlightBlacklistModel.hpp"
#include "controllers/highlights/HighlightController.hpp"
#include "controllers/highlights/HighlightModel.hpp"
#include "controllers/highlights/UserHighlightModel.hpp"
#include "singletons/Settings.hpp"
@@ -29,9 +28,7 @@
namespace chatterino {
HighlightingPage::HighlightingPage()
: SettingsPage("Highlights", ":/settings/notifications.svg")
{
auto app = getApp();
LayoutCreator<HighlightingPage> layoutCreator(this);
auto layout = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
@@ -47,16 +44,16 @@ HighlightingPage::HighlightingPage()
auto highlights = tabs.appendTab(new QVBoxLayout, "Messages");
{
highlights.emplace<QLabel>(
"Messages can be highlighted if they match a certain "
"pattern.\n"
"NOTE: User highlights will override phrase highlights.");
"Play notification sounds and highlight messages based on "
"certain patterns.");
EditableModelView *view =
auto view =
highlights
.emplace<EditableModelView>(
app->highlights->createModel(nullptr))
(new HighlightModel(nullptr))
->initialized(
&getSettings()->highlightedMessages))
.getElement();
view->addRegexHelpLink();
view->setTitles({"Pattern", "Flash\ntaskbar", "Play\nsound",
"Enable\nregex", "Case-\nsensitive",
@@ -74,7 +71,7 @@ HighlightingPage::HighlightingPage()
});
view->addButtonPressed.connect([] {
getApp()->highlights->phrases.appendItem(HighlightPhrase{
getSettings()->highlightedMessages.append(HighlightPhrase{
"my phrase", true, false, false, false, "",
*ColorProvider::instance().color(
ColorType::SelfHighlight)});
@@ -89,12 +86,15 @@ HighlightingPage::HighlightingPage()
auto pingUsers = tabs.appendTab(new QVBoxLayout, "Users");
{
pingUsers.emplace<QLabel>(
"Messages from a certain user can be highlighted.\n"
"NOTE: User highlights will override phrase highlights.");
"Play notification sounds and highlight messages from "
"certain users.\n"
"User highlights are prioritized over message "
"highlights.");
EditableModelView *view =
pingUsers
.emplace<EditableModelView>(
app->highlights->createUserModel(nullptr))
(new UserHighlightModel(nullptr))
->initialized(&getSettings()->highlightedUsers))
.getElement();
view->addRegexHelpLink();
@@ -118,11 +118,10 @@ HighlightingPage::HighlightingPage()
});
view->addButtonPressed.connect([] {
getApp()->highlights->highlightedUsers.appendItem(
HighlightPhrase{"highlighted user", true, false, false,
false, "",
*ColorProvider::instance().color(
ColorType::SelfHighlight)});
getSettings()->highlightedUsers.append(HighlightPhrase{
"highlighted user", true, false, false, false, "",
*ColorProvider::instance().color(
ColorType::SelfHighlight)});
});
QObject::connect(view->getTableView(), &QTableView::clicked,
@@ -132,19 +131,20 @@ HighlightingPage::HighlightingPage()
}
auto disabledUsers =
tabs.appendTab(new QVBoxLayout, "Excluded Users");
tabs.appendTab(new QVBoxLayout, "Blacklisted Users");
{
disabledUsers.emplace<QLabel>(
"This is a list of users (e.g. bots) whose messages should "
"<u>not</u> be highlighted.");
"Disable notification sounds and highlights from certain "
"users (e.g. bots).");
EditableModelView *view =
disabledUsers
.emplace<EditableModelView>(
app->highlights->createBlacklistModel(nullptr))
(new HighlightBlacklistModel(nullptr))
->initialized(&getSettings()->blacklistedUsers))
.getElement();
view->addRegexHelpLink();
view->setTitles({"Pattern", "Enable\nregex"});
view->setTitles({"Username", "Enable\nregex"});
view->getTableView()->horizontalHeader()->setSectionResizeMode(
QHeaderView::Fixed);
view->getTableView()->horizontalHeader()->setSectionResizeMode(
@@ -158,7 +158,7 @@ HighlightingPage::HighlightingPage()
});
view->addButtonPressed.connect([] {
getApp()->highlights->blacklistedUsers.appendItem(
getSettings()->blacklistedUsers.append(
HighlightBlacklistUser{"blacklisted user", false});
});
}
@@ -205,7 +205,7 @@ HighlightingPage::HighlightingPage()
// ---- misc
this->disabledUsersChangedTimer_.setSingleShot(true);
}
} // namespace chatterino
void HighlightingPage::tableCellClicked(const QModelIndex &clicked,
EditableModelView *view)
@@ -227,7 +227,8 @@ void HighlightingPage::tableCellClicked(const QModelIndex &clicked,
Qt::CheckStateRole);
}
}
else if (clicked.column() == Column::Color)
else if (clicked.column() == Column::Color &&
clicked.row() != HighlightModel::WHISPER_ROW)
{
auto initial =
view->getModel()->data(clicked, Qt::DecorationRole).value<QColor>();
+5 -5
View File
@@ -29,7 +29,6 @@ static void addUsersTab(IgnoresPage &page, LayoutCreator<QVBoxLayout> box,
QStringListModel &model);
IgnoresPage::IgnoresPage()
: SettingsPage("Ignores", ":/settings/ignore.svg")
{
LayoutCreator<IgnoresPage> layoutCreator(this);
auto layout = layoutCreator.setLayoutType<QVBoxLayout>();
@@ -42,11 +41,12 @@ IgnoresPage::IgnoresPage()
void addPhrasesTab(LayoutCreator<QVBoxLayout> layout)
{
layout.emplace<QLabel>("Messages can be ignored if "
"they match a certain pattern.");
layout.emplace<QLabel>("Ignore messages based certain patterns.");
EditableModelView *view =
layout
.emplace<EditableModelView>(getApp()->ignores->createModel(nullptr))
.emplace<EditableModelView>(
(new IgnoreModel(nullptr))
->initialized(&getSettings()->ignoredMessages))
.getElement();
view->setTitles(
{"Pattern", "Regex", "Case Sensitive", "Block", "Replacement"});
@@ -62,7 +62,7 @@ void addPhrasesTab(LayoutCreator<QVBoxLayout> layout)
});
view->addButtonPressed.connect([] {
getApp()->ignores->phrases.appendItem(
getSettings()->ignoredMessages.append(
IgnorePhrase{"my pattern", false, false,
getSettings()->ignoredPhraseReplace.getValue(), true});
});
@@ -8,7 +8,6 @@
namespace chatterino {
KeyboardSettingsPage::KeyboardSettingsPage()
: SettingsPage("Keybindings", ":/settings/keybinds.svg")
{
auto layout =
LayoutCreator<KeyboardSettingsPage>(this).setLayoutType<QVBoxLayout>();
@@ -26,10 +25,8 @@ KeyboardSettingsPage::KeyboardSettingsPage()
form->addRow(new QLabel("Ctrl + Shift + T"), new QLabel("Create new tab"));
form->addRow(new QLabel("Ctrl + Shift + W"),
new QLabel("Close current tab"));
form->addRow(
new QLabel("Ctrl + H"),
new QLabel(
"Hide/Show similar messages (Enable in General under Similarity)"));
form->addRow(new QLabel("Ctrl + H"),
new QLabel("Hide/Show similar messages (See General->R9K)"));
form->addItem(new QSpacerItem(16, 16));
form->addRow(new QLabel("Ctrl + 1/2/3/..."),
+3 -5
View File
@@ -2,8 +2,6 @@
#include "Application.hpp"
#include "controllers/moderationactions/ModerationActionModel.hpp"
#include "controllers/moderationactions/ModerationActions.hpp"
#include "controllers/taggedusers/TaggedUsersController.hpp"
#include "controllers/taggedusers/TaggedUsersModel.hpp"
#include "singletons/Logging.hpp"
#include "singletons/Paths.hpp"
@@ -73,7 +71,6 @@ QString fetchLogDirectorySize()
}
ModerationPage::ModerationPage()
: SettingsPage("Moderation", ":/settings/moderation.svg")
{
auto app = getApp();
LayoutCreator<ModerationPage> layoutCreator(this);
@@ -175,7 +172,8 @@ ModerationPage::ModerationPage()
EditableModelView *view =
modMode
.emplace<EditableModelView>(
app->moderationActions->createModel(nullptr))
(new ModerationActionModel(nullptr))
->initialized(&getSettings()->moderationActions))
.getElement();
view->setTitles({"Actions"});
@@ -185,7 +183,7 @@ ModerationPage::ModerationPage()
0, QHeaderView::Stretch);
view->addButtonPressed.connect([] {
getApp()->moderationActions->items.appendItem(
getSettings()->moderationActions.append(
ModerationAction("/timeout {user} 300"));
});
@@ -21,7 +21,6 @@
namespace chatterino {
NotificationPage::NotificationPage()
: SettingsPage("Notifications", ":/settings/notification2.svg")
{
LayoutCreator<NotificationPage> layoutCreator(this);
auto layout = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
@@ -101,7 +100,7 @@ NotificationPage::NotificationPage()
view->addButtonPressed.connect([] {
getApp()
->notifications->channelMap[Platform::Twitch]
.appendItem("channel");
.append("channel");
});
}
/*
+2 -15
View File
@@ -64,26 +64,13 @@ bool filterItemsRec(QObject *object, const QString &query)
return any;
}
SettingsPage::SettingsPage(const QString &name, const QString &iconResource)
: name_(name)
, iconResource_(iconResource)
SettingsPage::SettingsPage()
{
}
bool SettingsPage::filterElements(const QString &query)
{
return filterItemsRec(this, query) || query.isEmpty() ||
this->name_.contains(query, Qt::CaseInsensitive);
}
const QString &SettingsPage::getName()
{
return this->name_;
}
const QString &SettingsPage::getIconResource()
{
return this->iconResource_;
return filterItemsRec(this, query) || query.isEmpty();
}
SettingsDialogTab *SettingsPage::tab() const
+1 -8
View File
@@ -47,10 +47,7 @@ class SettingsPage : public QFrame
Q_OBJECT
public:
SettingsPage(const QString &name, const QString &iconResource);
const QString &getName();
const QString &getIconResource();
SettingsPage();
virtual bool filterElements(const QString &query);
@@ -72,11 +69,7 @@ public:
}
protected:
QString name_;
QString iconResource_;
SettingsDialogTab *tab_;
pajlada::Signals::NoArgSignal onCancel_;
std::vector<pajlada::Signals::ScopedConnection> managedConnections_;
};
+17
View File
@@ -541,6 +541,23 @@ void Split::openInStreamlink()
}
}
void Split::openWithCustomScheme()
{
const auto scheme = getSettings()->customURIScheme.getValue();
if (scheme.isEmpty())
{
return;
}
const auto channel = this->getChannel().get();
if (const auto twitchChannel = dynamic_cast<TwitchChannel *>(channel))
{
QDesktopServices::openUrl(QString("%1https://twitch.tv/%2")
.arg(scheme)
.arg(twitchChannel->getName()));
}
}
void Split::showViewerList()
{
auto viewerDock = new QDockWidget("Viewer List", this);
+1
View File
@@ -129,6 +129,7 @@ public slots:
void openInBrowser();
void openBrowserPlayer();
void openInStreamlink();
void openWithCustomScheme();
void copyToClipboard();
void showSearch();
void showViewerList();
+24 -10
View File
@@ -2,9 +2,7 @@
#include "Application.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "controllers/moderationactions/ModerationActions.hpp"
#include "controllers/notifications/NotificationController.hpp"
#include "controllers/pings/PingController.hpp"
#include "providers/twitch/TwitchChannel.hpp"
#include "providers/twitch/TwitchIrcServer.hpp"
#include "singletons/Resources.hpp"
@@ -96,8 +94,11 @@ namespace {
.arg(s.game.toHtmlEscaped())
.arg(s.game.isEmpty() ? QString() : "<br>")
.arg(s.rerun ? "Vod-casting" : "Live")
.arg(s.uptime)
.arg(QString::number(s.viewerCount));
.arg(getSettings()->hideViewerCountAndDuration ? "&lt;Hidden&gt;"
: s.uptime)
.arg(getSettings()->hideViewerCountAndDuration
? "&lt;Hidden&gt;"
: QString::number(s.viewerCount));
}
auto formatOfflineTooltip(const TwitchChannel::StreamStatus &s)
{
@@ -189,7 +190,7 @@ void SplitHeader::initializeLayout()
switch (button)
{
case Qt::LeftButton:
if (getApp()->moderationActions->items.empty())
if (getSettings()->moderationActions.empty())
{
getApp()->windows->showSettingsDialog(
SettingsDialogPreference::
@@ -233,9 +234,9 @@ void SplitHeader::initializeLayout()
});
// update moderation button when items changed
this->managedConnect(getApp()->moderationActions->items.delayedItemsChanged,
this->managedConnect(getSettings()->moderationActions.delayedItemsChanged,
[this] {
if (getApp()->moderationActions->items.empty())
if (getSettings()->moderationActions.empty())
{
if (this->split_->getModerationMode())
this->split_->setModerationMode(true);
@@ -247,6 +248,13 @@ void SplitHeader::initializeLayout()
}
});
getSettings()->customURIScheme.connect([this] {
if (const auto drop = this->dropdownButton_)
{
drop->setMenu(this->createMainMenu());
}
});
layout->setMargin(0);
layout->setSpacing(0);
this->setLayout(layout);
@@ -291,6 +299,12 @@ std::unique_ptr<QMenu> SplitHeader::createMainMenu()
#endif
menu->addAction(OPEN_IN_STREAMLINK, this->split_,
&Split::openInStreamlink);
if (!getSettings()->customURIScheme.getValue().isEmpty())
{
menu->addAction("Open with URI Scheme", this->split_,
&Split::openWithCustomScheme);
}
menu->addSeparator();
}
@@ -339,11 +353,11 @@ std::unique_ptr<QMenu> SplitHeader::createMainMenu()
action->setCheckable(true);
QObject::connect(moreMenu, &QMenu::aboutToShow, this, [action, this]() {
action->setChecked(getApp()->pings->isMuted(
action->setChecked(getSettings()->isMutedChannel(
this->split_->getChannel()->getName()));
});
action->connect(action, &QAction::triggered, this, [this]() {
getApp()->pings->toggleMuteChannel(
getSettings()->toggleMutedChannel(
this->split_->getChannel()->getName());
});
@@ -568,7 +582,7 @@ void SplitHeader::updateChannelText()
void SplitHeader::updateModerationModeIcon()
{
auto moderationMode = this->split_->getModerationMode() &&
!getApp()->moderationActions->items.empty();
!getSettings()->moderationActions.empty();
this->moderationButton_->setPixmap(
moderationMode ? getResources().buttons.modModeEnabled
+16 -8
View File
@@ -9,6 +9,7 @@
#include "singletons/Theme.hpp"
#include "util/LayoutCreator.hpp"
#include "widgets/Notebook.hpp"
#include "widgets/Scrollbar.hpp"
#include "widgets/dialogs/EmotePopup.hpp"
#include "widgets/helper/ChannelView.hpp"
#include "widgets/helper/EffectLabel.hpp"
@@ -267,14 +268,21 @@ void SplitInput::installKeyPressedEvent()
}
else if (event->key() == Qt::Key_End)
{
QTextCursor cursor = this->ui_.textEdit->textCursor();
cursor.movePosition(
QTextCursor::End,
event->modifiers() & Qt::KeyboardModifier::ShiftModifier
? QTextCursor::MoveMode::KeepAnchor
: QTextCursor::MoveMode::MoveAnchor);
this->ui_.textEdit->setTextCursor(cursor);
if (event->modifiers() == Qt::ControlModifier)
{
this->split_->getChannelView().getScrollBar().scrollToBottom(
getSettings()->enableSmoothScrollingNewMessages.getValue());
}
else
{
QTextCursor cursor = this->ui_.textEdit->textCursor();
cursor.movePosition(
QTextCursor::End,
event->modifiers() & Qt::KeyboardModifier::ShiftModifier
? QTextCursor::MoveMode::KeepAnchor
: QTextCursor::MoveMode::MoveAnchor);
this->ui_.textEdit->setTextCursor(cursor);
}
event->accept();
}
else if (event->key() == Qt::Key_H &&
-2
View File
@@ -158,8 +158,6 @@ void SplitOverlay::mouseMoveEvent(QMouseEvent *event)
{
BaseWidget::mouseMoveEvent(event);
// qDebug() << QGuiApplication::queryKeyboardModifiers();
// if ((QGuiApplication::queryKeyboardModifiers() & Qt::AltModifier) ==
// Qt::AltModifier) {
// this->hide();