added regex highlights

This commit is contained in:
fourtf 2018-05-06 12:52:47 +02:00
parent ba4173822e
commit b95388107f
24 changed files with 388 additions and 139 deletions

View file

@ -191,7 +191,10 @@ SOURCES += \
src/controllers/commands/commandcontroller.cpp \ src/controllers/commands/commandcontroller.cpp \
src/controllers/highlights/highlightcontroller.cpp \ src/controllers/highlights/highlightcontroller.cpp \
src/controllers/highlights/highlightmodel.cpp \ src/controllers/highlights/highlightmodel.cpp \
src/widgets/helper/editablemodelview.cpp src/widgets/helper/editablemodelview.cpp \
src/controllers/accounts/accountcontroller.cpp \
src/controllers/accounts/accountmodel.cpp \
src/controllers/accounts/account.cpp
HEADERS += \ HEADERS += \
src/precompiled_header.hpp \ src/precompiled_header.hpp \
@ -330,7 +333,11 @@ HEADERS += \
src/controllers/highlights/highlightcontroller.hpp \ src/controllers/highlights/highlightcontroller.hpp \
src/controllers/highlights/highlightphrase.hpp \ src/controllers/highlights/highlightphrase.hpp \
src/controllers/highlights/highlightmodel.hpp \ src/controllers/highlights/highlightmodel.hpp \
src/widgets/helper/editablemodelview.hpp src/widgets/helper/editablemodelview.hpp \
src/controllers/accounts/accountcontroller.hpp \
src/controllers/accounts/accountmodel.hpp \
src/controllers/accounts/account.hpp \
src/util/sharedptrelementless.hpp
RESOURCES += \ RESOURCES += \
resources/resources.qrc resources/resources.qrc

View file

@ -203,7 +203,7 @@ void Application::initialize()
this->twitch.pubsub->listenToWhispers(this->accounts->Twitch.getCurrent()); // this->twitch.pubsub->listenToWhispers(this->accounts->Twitch.getCurrent()); //
}; };
this->accounts->Twitch.userChanged.connect(RequestModerationActions); this->accounts->Twitch.currentUserChanged.connect(RequestModerationActions);
RequestModerationActions(); RequestModerationActions();
} }

View file

@ -0,0 +1,30 @@
#include "account.hpp"
namespace chatterino {
namespace controllers {
namespace accounts {
Account::Account(const QString &category)
{
}
const QString &Account::getCategory() const
{
return this->category;
}
bool Account::operator<(const Account &other) const
{
if (this->category < other.category) {
return true;
} else if (this->category == other.category) {
if (this->toString() < other.toString()) {
return true;
}
}
return false;
}
} // namespace accounts
} // namespace controllers
} // namespace chatterino

View file

@ -0,0 +1,25 @@
#pragma once
#include <QString>
namespace chatterino {
namespace controllers {
namespace accounts {
class Account
{
public:
Account(const QString &category);
virtual QString toString() const = 0;
const QString &getCategory() const;
bool operator<(const Account &other) const;
private:
QString category;
};
} // namespace accounts
} // namespace controllers
} // namespace chatterino

View file

@ -0,0 +1,24 @@
#include "accountcontroller.hpp"
#include "controllers/accounts/accountmodel.hpp"
namespace chatterino {
namespace controllers {
namespace accounts {
AccountController::AccountController()
{
}
AccountModel *AccountController::createModel(QObject *parent)
{
AccountModel *model = new AccountModel(parent);
//(util::BaseSignalVector<stdAccount> *)
model->init(&this->accounts);
return model;
}
} // namespace accounts
} // namespace controllers
} // namespace chatterino

View file

@ -0,0 +1,29 @@
#pragma once
#include <QObject>
#include "controllers/accounts/account.hpp"
#include "util/sharedptrelementless.hpp"
#include "util/signalvector2.hpp"
namespace chatterino {
namespace controllers {
namespace accounts {
class AccountModel;
class AccountController
{
public:
AccountController();
AccountModel *createModel(QObject *parent);
private:
util::SortedSignalVector<std::shared_ptr<Account>, util::SharedPtrElementLess<Account>>
accounts;
};
} // namespace accounts
} // namespace controllers
} // namespace chatterino

View file

@ -0,0 +1,27 @@
#include "accountmodel.hpp"
namespace chatterino {
namespace controllers {
namespace accounts {
AccountModel::AccountModel(QObject *parent)
: util::SignalVectorModel<std::shared_ptr<Account>>(1, parent)
{
}
// turn a vector item into a model row
std::shared_ptr<Account> AccountModel::getItemFromRow(std::vector<QStandardItem *> &row)
{
return nullptr;
}
// turns a row in the model into a vector item
void AccountModel::getRowFromItem(const std::shared_ptr<Account> &item,
std::vector<QStandardItem *> &row)
{
row[0]->setData(item->toString(), Qt::DisplayRole);
}
} // namespace accounts
} // namespace controllers
} // namespace chatterino

View file

@ -0,0 +1,30 @@
#pragma
#include "controllers/accounts/account.hpp"
#include "util/signalvectormodel.hpp"
namespace chatterino {
namespace controllers {
namespace accounts {
class AccountController;
class AccountModel : public util::SignalVectorModel<std::shared_ptr<Account>>
{
public:
AccountModel(QObject *parent);
protected:
// turn a vector item into a model row
virtual std::shared_ptr<Account> getItemFromRow(std::vector<QStandardItem *> &row) override;
// turns a row in the model into a vector item
virtual void getRowFromItem(const std::shared_ptr<Account> &item,
std::vector<QStandardItem *> &row) override;
friend class AccountController;
};
} // namespace accounts
} // namespace controllers
} // namespace chatterino

View file

@ -45,10 +45,10 @@ HighlightPhrase HighlightModel::getItemFromRow(std::vector<QStandardItem *> &row
// turns a row in the model into a vector item // turns a row in the model into a vector item
void HighlightModel::getRowFromItem(const HighlightPhrase &item, std::vector<QStandardItem *> &row) void HighlightModel::getRowFromItem(const HighlightPhrase &item, std::vector<QStandardItem *> &row)
{ {
util::setStringItem(row[0], item.key); util::setStringItem(row[0], item.getPattern());
util::setBoolItem(row[1], item.alert); util::setBoolItem(row[1], item.getAlert());
util::setBoolItem(row[2], item.sound); util::setBoolItem(row[2], item.getSound());
util::setBoolItem(row[3], item.regex); util::setBoolItem(row[3], item.isRegex());
} }
void HighlightModel::afterInit() void HighlightModel::afterInit()

View file

@ -2,24 +2,71 @@
#include "util/serialize-custom.hpp" #include "util/serialize-custom.hpp"
#include <QRegularExpression>
#include <QString> #include <QString>
#include <memory>
#include <pajlada/settings/serialize.hpp> #include <pajlada/settings/serialize.hpp>
namespace chatterino { namespace chatterino {
namespace controllers { namespace controllers {
namespace highlights { namespace highlights {
struct HighlightPhrase { class HighlightPhrase
QString key; {
QString pattern;
bool alert; bool alert;
bool sound; bool sound;
bool regex; bool _isRegex;
QRegularExpression regex;
public:
bool operator==(const HighlightPhrase &other) const bool operator==(const HighlightPhrase &other) const
{ {
return std::tie(this->key, this->sound, this->alert, this->regex) == return std::tie(this->pattern, this->sound, this->alert, this->_isRegex) ==
std::tie(other.key, other.sound, other.alert, other.regex); std::tie(other.pattern, other.sound, other.alert, other._isRegex);
} }
HighlightPhrase(const QString &_pattern, bool _alert, bool _sound, bool isRegex)
: pattern(_pattern)
, alert(_alert)
, sound(_sound)
, _isRegex(isRegex)
, regex(_isRegex ? _pattern : "\b" + QRegularExpression::escape(_pattern) + "\b",
QRegularExpression::CaseInsensitiveOption)
{
}
const QString &getPattern() const
{
return this->pattern;
}
bool getAlert() const
{
return this->alert;
}
bool getSound() const
{
return this->sound;
}
bool isRegex() const
{
return this->_isRegex;
}
bool isValid() const
{
return !this->pattern.isEmpty() && this->regex.isValid();
}
bool isMatch(const QString &subject) const
{
return this->isValid() && this->regex.match(subject).hasMatch();
}
// const QRegularExpression &getRegex() const
// {
// return this->regex;
// }
}; };
} // namespace highlights } // namespace highlights
} // namespace controllers } // namespace controllers
@ -35,10 +82,10 @@ struct Serialize<chatterino::controllers::highlights::HighlightPhrase> {
{ {
rapidjson::Value ret(rapidjson::kObjectType); rapidjson::Value ret(rapidjson::kObjectType);
AddMember(ret, "key", value.key, a); AddMember(ret, "pattern", value.getPattern(), a);
AddMember(ret, "alert", value.alert, a); AddMember(ret, "alert", value.getAlert(), a);
AddMember(ret, "sound", value.sound, a); AddMember(ret, "sound", value.getSound(), a);
AddMember(ret, "regex", value.regex, a); AddMember(ret, "regex", value.isRegex(), a);
return ret; return ret;
} }
@ -48,40 +95,45 @@ template <>
struct Deserialize<chatterino::controllers::highlights::HighlightPhrase> { struct Deserialize<chatterino::controllers::highlights::HighlightPhrase> {
static chatterino::controllers::highlights::HighlightPhrase get(const rapidjson::Value &value) static chatterino::controllers::highlights::HighlightPhrase get(const rapidjson::Value &value)
{ {
chatterino::controllers::highlights::HighlightPhrase ret;
if (!value.IsObject()) { if (!value.IsObject()) {
return ret; return chatterino::controllers::highlights::HighlightPhrase(QString(), true, false,
false);
} }
if (value.HasMember("key")) { QString _pattern;
const rapidjson::Value &key = value["key"]; if (value.HasMember("pattern")) {
const rapidjson::Value &key = value["pattern"];
if (key.IsString()) { if (key.IsString()) {
ret.key = key.GetString(); _pattern = key.GetString();
} }
} }
bool _alert = true;
if (value.HasMember("alert")) { if (value.HasMember("alert")) {
const rapidjson::Value &alert = value["alert"]; const rapidjson::Value &alert = value["alert"];
if (alert.IsBool()) { if (alert.IsBool()) {
ret.alert = alert.GetBool(); _alert = alert.GetBool();
} }
} }
bool _sound = false;
if (value.HasMember("sound")) { if (value.HasMember("sound")) {
const rapidjson::Value &sound = value["sound"]; const rapidjson::Value &sound = value["sound"];
if (sound.IsBool()) { if (sound.IsBool()) {
ret.sound = sound.GetBool(); _sound = sound.GetBool();
} }
} }
bool _isRegex = false;
if (value.HasMember("regex")) { if (value.HasMember("regex")) {
const rapidjson::Value &regex = value["regex"]; const rapidjson::Value &regex = value["regex"];
if (regex.IsBool()) { if (regex.IsBool()) {
ret.regex = regex.GetBool(); _isRegex = regex.GetBool();
} }
} }
return ret; return chatterino::controllers::highlights::HighlightPhrase(_pattern, _alert, _sound,
_isRegex);
} }
}; };

View file

@ -61,77 +61,79 @@ void IrcMessageHandler::handleRoomStateMessage(Communi::IrcMessage *message)
void IrcMessageHandler::handleClearChatMessage(Communi::IrcMessage *message) void IrcMessageHandler::handleClearChatMessage(Communi::IrcMessage *message)
{ {
return; return;
// check parameter count // // check parameter count
if (message->parameters().length() < 1) { // if (message->parameters().length() < 1) {
return; // return;
} // }
QString chanName; // QString chanName;
if (!TrimChannelName(message->parameter(0), chanName)) { // if (!TrimChannelName(message->parameter(0), chanName)) {
return; // return;
} // }
auto app = getApp(); // auto app = getApp();
// get channel // // get channel
auto chan = app->twitch.server->getChannelOrEmpty(chanName); // auto chan = app->twitch.server->getChannelOrEmpty(chanName);
if (chan->isEmpty()) { // if (chan->isEmpty()) {
debug::Log("[IrcMessageHandler:handleClearChatMessage] Twitch channel {} not found", // debug::Log("[IrcMessageHandler:handleClearChatMessage] Twitch channel {} not found",
chanName); // chanName);
return; // return;
} // }
// check if the chat has been cleared by a moderator // // check if the chat has been cleared by a moderator
if (message->parameters().length() == 1) { // if (message->parameters().length() == 1) {
chan->addMessage(Message::createSystemMessage("Chat has been cleared by a moderator.")); // chan->addMessage(Message::createSystemMessage("Chat has been cleared by a
// moderator."));
return; // return;
} // }
// get username, duration and message of the timed out user // // get username, duration and message of the timed out user
QString username = message->parameter(1); // QString username = message->parameter(1);
QString durationInSeconds, reason; // QString durationInSeconds, reason;
QVariant v = message->tag("ban-duration"); // QVariant v = message->tag("ban-duration");
if (v.isValid()) { // if (v.isValid()) {
durationInSeconds = v.toString(); // durationInSeconds = v.toString();
} // }
v = message->tag("ban-reason"); // v = message->tag("ban-reason");
if (v.isValid()) { // if (v.isValid()) {
reason = v.toString(); // reason = v.toString();
} // }
// add the notice that the user has been timed out // // add the notice that the user has been timed out
LimitedQueueSnapshot<MessagePtr> snapshot = chan->getMessageSnapshot(); // LimitedQueueSnapshot<MessagePtr> snapshot = chan->getMessageSnapshot();
bool addMessage = true; // bool addMessage = true;
int snapshotLength = snapshot.getLength(); // int snapshotLength = snapshot.getLength();
for (int i = std::max(0, snapshotLength - 20); i < snapshotLength; i++) { // for (int i = std::max(0, snapshotLength - 20); i < snapshotLength; i++) {
auto &s = snapshot[i]; // auto &s = snapshot[i];
if (s->flags.HasFlag(Message::Timeout) && s->timeoutUser == username) { // if (s->flags.HasFlag(Message::Timeout) && s->timeoutUser == username) {
MessagePtr replacement( // MessagePtr replacement(
Message::createTimeoutMessage(username, durationInSeconds, reason, true)); // Message::createTimeoutMessage(username, durationInSeconds, reason, true));
chan->replaceMessage(s, replacement); // chan->replaceMessage(s, replacement);
addMessage = false; // addMessage = false;
break; // break;
} // }
} // }
if (addMessage) { // if (addMessage) {
chan->addMessage(Message::createTimeoutMessage(username, durationInSeconds, reason, false)); // chan->addMessage(Message::createTimeoutMessage(username, durationInSeconds, reason,
} // false));
// }
// disable the messages from the user // // disable the messages from the user
for (int i = 0; i < snapshotLength; i++) { // for (int i = 0; i < snapshotLength; i++) {
auto &s = snapshot[i]; // auto &s = snapshot[i];
if (!(s->flags & Message::Timeout) && s->loginName == username) { // if (!(s->flags & Message::Timeout) && s->loginName == username) {
s->flags.EnableFlag(Message::Disabled); // s->flags.EnableFlag(Message::Disabled);
} // }
} // }
// refresh all // // refresh all
app->windows->repaintVisibleChatWidgets(chan.get()); // app->windows->repaintVisibleChatWidgets(chan.get());
} }
void IrcMessageHandler::handleUserStateMessage(Communi::IrcMessage *message) void IrcMessageHandler::handleUserStateMessage(Communi::IrcMessage *message)
@ -213,28 +215,28 @@ void IrcMessageHandler::handleModeMessage(Communi::IrcMessage *message)
void IrcMessageHandler::handleNoticeMessage(Communi::IrcNoticeMessage *message) void IrcMessageHandler::handleNoticeMessage(Communi::IrcNoticeMessage *message)
{ {
return; return;
auto app = getApp(); // auto app = getApp();
MessagePtr msg = Message::createSystemMessage(message->content()); // MessagePtr msg = Message::createSystemMessage(message->content());
QString channelName; // QString channelName;
if (!TrimChannelName(message->target(), channelName)) { // if (!TrimChannelName(message->target(), channelName)) {
// Notice wasn't targeted at a single channel, send to all twitch channels // // Notice wasn't targeted at a single channel, send to all twitch channels
app->twitch.server->forEachChannelAndSpecialChannels([msg](const auto &c) { // app->twitch.server->forEachChannelAndSpecialChannels([msg](const auto &c) {
c->addMessage(msg); // // c->addMessage(msg); //
}); // });
return; // return;
} // }
auto channel = app->twitch.server->getChannelOrEmpty(channelName); // auto channel = app->twitch.server->getChannelOrEmpty(channelName);
if (channel->isEmpty()) { // if (channel->isEmpty()) {
debug::Log("[IrcManager:handleNoticeMessage] Channel {} not found in channel manager", // debug::Log("[IrcManager:handleNoticeMessage] Channel {} not found in channel manager",
channelName); // channelName);
return; // return;
} // }
channel->addMessage(msg); // channel->addMessage(msg);
} }
void IrcMessageHandler::handleWriteConnectionNoticeMessage(Communi::IrcNoticeMessage *message) void IrcMessageHandler::handleWriteConnectionNoticeMessage(Communi::IrcNoticeMessage *message)

View file

@ -7,7 +7,8 @@ namespace twitch {
TwitchAccount::TwitchAccount(const QString &_username, const QString &_oauthToken, TwitchAccount::TwitchAccount(const QString &_username, const QString &_oauthToken,
const QString &_oauthClient, const QString &_userID) const QString &_oauthClient, const QString &_userID)
: oauthClient(_oauthClient) : controllers::accounts::Account("Twitch")
, oauthClient(_oauthClient)
, oauthToken(_oauthToken) , oauthToken(_oauthToken)
, userName(_username) , userName(_username)
, userId(_userID) , userId(_userID)
@ -15,6 +16,11 @@ TwitchAccount::TwitchAccount(const QString &_username, const QString &_oauthToke
{ {
} }
QString TwitchAccount::toString() const
{
return this->getUserName();
}
const QString &TwitchAccount::getUserName() const const QString &TwitchAccount::getUserName() const
{ {
return this->userName; return this->userName;

View file

@ -3,16 +3,20 @@
#include <QColor> #include <QColor>
#include <QString> #include <QString>
#include "controllers/accounts/account.hpp"
namespace chatterino { namespace chatterino {
namespace providers { namespace providers {
namespace twitch { namespace twitch {
class TwitchAccount class TwitchAccount : public controllers::accounts::Account
{ {
public: public:
TwitchAccount(const QString &username, const QString &oauthToken, const QString &oauthClient, TwitchAccount(const QString &username, const QString &oauthToken, const QString &oauthClient,
const QString &_userID); const QString &_userID);
virtual QString toString() const override;
const QString &getUserName() const; const QString &getUserName() const;
const QString &getOAuthToken() const; const QString &getOAuthToken() const;
const QString &getOAuthClient() const; const QString &getOAuthClient() const;

View file

@ -94,7 +94,7 @@ void TwitchAccountManager::reloadUsers()
debug::Log("User {} already exists, and values updated!", userData.username); debug::Log("User {} already exists, and values updated!", userData.username);
if (userData.username == this->getCurrent()->getUserName()) { if (userData.username == this->getCurrent()->getUserName()) {
debug::Log("It was the current user, so we need to reconnect stuff!"); debug::Log("It was the current user, so we need to reconnect stuff!");
this->userChanged.invoke(); this->currentUserChanged.invoke();
} }
} break; } break;
case AddUserResponse::UserAdded: { case AddUserResponse::UserAdded: {
@ -126,7 +126,7 @@ void TwitchAccountManager::load()
this->currentUser = this->anonymousUser; this->currentUser = this->anonymousUser;
} }
this->userChanged.invoke(); this->currentUserChanged.invoke();
}); });
} }

View file

@ -1,6 +1,8 @@
#pragma once #pragma once
#include "providers/twitch/twitchaccount.hpp" #include "providers/twitch/twitchaccount.hpp"
#include "util/sharedptrelementless.hpp"
#include "util/signalvector2.hpp"
#include <pajlada/settings/setting.hpp> #include <pajlada/settings/setting.hpp>
@ -47,9 +49,13 @@ public:
bool removeUser(const QString &username); bool removeUser(const QString &username);
pajlada::Settings::Setting<std::string> currentUsername = {"/accounts/current", ""}; pajlada::Settings::Setting<std::string> currentUsername = {"/accounts/current", ""};
pajlada::Signals::NoArgSignal userChanged; pajlada::Signals::NoArgSignal currentUserChanged;
pajlada::Signals::NoArgSignal userListUpdated; pajlada::Signals::NoArgSignal userListUpdated;
util::SortedSignalVector<std::shared_ptr<TwitchAccount>,
util::SharedPtrElementLess<TwitchAccount>>
accounts;
private: private:
enum class AddUserResponse { enum class AddUserResponse {
UserAlreadyExists, UserAlreadyExists,

View file

@ -45,7 +45,7 @@ TwitchChannel::TwitchChannel(const QString &channelName, Communi::IrcConnection
this->refreshLiveStatus(); // this->refreshLiveStatus(); //
}); });
this->managedConnect(app->accounts->Twitch.userChanged, [this]() { this->setMod(false); }); this->managedConnect(app->accounts->Twitch.currentUserChanged, [this]() { this->setMod(false); });
auto refreshPubSubState = [=]() { auto refreshPubSubState = [=]() {
if (!this->hasModRights()) { if (!this->hasModRights()) {
@ -64,7 +64,7 @@ TwitchChannel::TwitchChannel(const QString &channelName, Communi::IrcConnection
this->userStateChanged.connect(refreshPubSubState); this->userStateChanged.connect(refreshPubSubState);
this->roomIDchanged.connect(refreshPubSubState); this->roomIDchanged.connect(refreshPubSubState);
this->managedConnect(app->accounts->Twitch.userChanged, refreshPubSubState); this->managedConnect(app->accounts->Twitch.currentUserChanged, refreshPubSubState);
refreshPubSubState(); refreshPubSubState();
this->fetchMessages.connect([this] { this->fetchMessages.connect([this] {

View file

@ -404,10 +404,9 @@ void TwitchMessageBuilder::parseHighlights()
app->highlights->phrases.getVector(); app->highlights->phrases.getVector();
if (app->settings->enableHighlightsSelf && currentUsername.size() > 0) { if (app->settings->enableHighlightsSelf && currentUsername.size() > 0) {
controllers::highlights::HighlightPhrase selfHighlight; controllers::highlights::HighlightPhrase selfHighlight(
selfHighlight.key = currentUsername; currentUsername, app->settings->enableHighlightTaskbar,
selfHighlight.sound = app->settings->enableHighlightSound; app->settings->enableHighlightSound, false);
selfHighlight.alert = app->settings->enableHighlightTaskbar;
activeHighlights.emplace_back(std::move(selfHighlight)); activeHighlights.emplace_back(std::move(selfHighlight));
} }
@ -419,26 +418,17 @@ void TwitchMessageBuilder::parseHighlights()
if (!blackList.contains(this->ircMessage->nick(), Qt::CaseInsensitive)) { if (!blackList.contains(this->ircMessage->nick(), Qt::CaseInsensitive)) {
for (const controllers::highlights::HighlightPhrase &highlight : activeHighlights) { for (const controllers::highlights::HighlightPhrase &highlight : activeHighlights) {
int index = -1; if (highlight.isMatch(this->originalMessage)) {
debug::Log("Highlight because {} matches {}", this->originalMessage,
while ((index = this->originalMessage.indexOf(highlight.key, index + 1, highlight.getPattern());
Qt::CaseInsensitive)) != -1) {
if ((index != 0 && this->originalMessage[index - 1] != ' ') ||
(index + highlight.key.length() != this->originalMessage.length() &&
this->originalMessage[index + highlight.key.length()] != ' ')) {
continue;
}
debug::Log("Highlight because {} contains {}", this->originalMessage,
highlight.key);
doHighlight = true; doHighlight = true;
if (highlight.sound) { if (highlight.getAlert()) {
playSound = true; doAlert = true;
} }
if (highlight.alert) { if (highlight.getSound()) {
doAlert = true; playSound = true;
} }
if (playSound && doAlert) { if (playSound && doAlert) {

View file

@ -27,7 +27,7 @@ TwitchServer::TwitchServer()
void TwitchServer::initialize() void TwitchServer::initialize()
{ {
getApp()->accounts->Twitch.userChanged.connect( getApp()->accounts->Twitch.currentUserChanged.connect(
[this]() { util::postToThread([this] { this->connect(); }); }); [this]() { util::postToThread([this] { this->connect(); }); });
} }

View file

@ -86,7 +86,7 @@ EmoteManager::EmoteManager()
void EmoteManager::initialize() void EmoteManager::initialize()
{ {
getApp()->accounts->Twitch.userChanged.connect([this] { getApp()->accounts->Twitch.currentUserChanged.connect([this] {
auto currentUser = getApp()->accounts->Twitch.getCurrent(); auto currentUser = getApp()->accounts->Twitch.getCurrent();
assert(currentUser); assert(currentUser);
this->refreshTwitchEmotes(currentUser); this->refreshTwitchEmotes(currentUser);

View file

@ -0,0 +1,17 @@
#pragma once
#include <memory>
namespace chatterino {
namespace util {
template <typename T>
struct SharedPtrElementLess {
bool operator()(const std::shared_ptr<T> &a, const std::shared_ptr<T> &b) const
{
return a->operator<(*b);
}
};
} // namespace util
} // namespace chatterino

View file

@ -103,16 +103,18 @@ public:
} }
}; };
template <typename TVectorItem> template <typename TVectorItem, typename Compare>
class SortedSignalVector : public BaseSignalVector<TVectorItem> class SortedSignalVector : public BaseSignalVector<TVectorItem>
{ {
public: public:
virtual int insertItem(const TVectorItem &item, int index = -1, void *caller = 0) override virtual int insertItem(const TVectorItem &item, int proposedIndex = -1,
void *caller = 0) override
{ {
util::assertInGuiThread(); util::assertInGuiThread();
int index = this->vector.insert( int index =
std::lower_bound(this->vector.begin(), this->vector.end(), item), item) - this->vector.insert(
std::lower_bound(this->vector.begin(), this->vector.end(), item, Compare{}), item) -
this->vector.begin(); this->vector.begin();
typename ReadOnlySignalVector<TVectorItem>::ItemArgs args{item, index, caller}; typename ReadOnlySignalVector<TVectorItem>::ItemArgs args{item, index, caller};
this->itemInserted.invoke(args); this->itemInserted.invoke(args);

View file

@ -40,7 +40,7 @@ AccountPopupWidget::AccountPopupWidget(ChannelPtr _channel)
connect(this, &AccountPopupWidget::refreshButtons, this, connect(this, &AccountPopupWidget::refreshButtons, this,
&AccountPopupWidget::actuallyRefreshButtons, Qt::QueuedConnection); &AccountPopupWidget::actuallyRefreshButtons, Qt::QueuedConnection);
app->accounts->Twitch.userChanged.connect([=] { app->accounts->Twitch.currentUserChanged.connect([=] {
auto currentTwitchUser = app->accounts->Twitch.getCurrent(); auto currentTwitchUser = app->accounts->Twitch.getCurrent();
if (!currentTwitchUser) { if (!currentTwitchUser) {
// No twitch user set (should never happen) // No twitch user set (should never happen)

View file

@ -47,15 +47,13 @@ HighlightingPage::HighlightingPage()
helper::EditableModelView *view = *highlights.emplace<helper::EditableModelView>( helper::EditableModelView *view = *highlights.emplace<helper::EditableModelView>(
app->highlights->createModel(nullptr)); app->highlights->createModel(nullptr));
view->getTableView()->hideColumn(3);
view->setTitles({"Pattern", "Flash taskbar", "Play sound", "Regex"}); view->setTitles({"Pattern", "Flash taskbar", "Play sound", "Regex"});
view->getTableView()->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed); view->getTableView()->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
// fourtf: make class extrend BaseWidget and add this to dpiChanged // fourtf: make class extrend BaseWidget and add this to dpiChanged
QTimer::singleShot(1, [view] { QTimer::singleShot(1, [view] {
view->getTableView()->resizeColumnsToContents(); view->getTableView()->resizeColumnsToContents();
view->getTableView()->setColumnWidth(0, 250); view->getTableView()->setColumnWidth(0, 200);
}); });
view->addButtonPressed.connect([] { view->addButtonPressed.connect([] {

View file

@ -47,7 +47,7 @@ Window::Window(WindowType _type)
app->windows->showAccountSelectPopup(QCursor::pos()); // app->windows->showAccountSelectPopup(QCursor::pos()); //
}); });
app->accounts->Twitch.userChanged.connect( app->accounts->Twitch.currentUserChanged.connect(
[=] { user->getLabel().setText(app->accounts->Twitch.getCurrent()->getUserName()); }); [=] { user->getLabel().setText(app->accounts->Twitch.getCurrent()->getUserName()); });
} }