mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
added template model/view magic for commands
This commit is contained in:
parent
e31dc09e91
commit
6bd787423d
14 changed files with 326 additions and 165 deletions
|
@ -184,7 +184,9 @@ SOURCES += \
|
||||||
src/widgets/lastruncrashdialog.cpp \
|
src/widgets/lastruncrashdialog.cpp \
|
||||||
src/widgets/attachedwindow.cpp \
|
src/widgets/attachedwindow.cpp \
|
||||||
src/widgets/settingspages/externaltoolspage.cpp \
|
src/widgets/settingspages/externaltoolspage.cpp \
|
||||||
src/widgets/helper/comboboxitemdelegate.cpp
|
src/widgets/helper/comboboxitemdelegate.cpp \
|
||||||
|
src/util/signalvectormodel.cpp \
|
||||||
|
src/managers/commands/command.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
src/precompiled_header.hpp \
|
src/precompiled_header.hpp \
|
||||||
|
@ -316,7 +318,9 @@ HEADERS += \
|
||||||
src/util/standarditemhelper.hpp \
|
src/util/standarditemhelper.hpp \
|
||||||
src/widgets/helper/comboboxitemdelegate.hpp \
|
src/widgets/helper/comboboxitemdelegate.hpp \
|
||||||
src/util/assertinguithread.hpp \
|
src/util/assertinguithread.hpp \
|
||||||
src/util/signalvector2.hpp
|
src/util/signalvector2.hpp \
|
||||||
|
src/util/signalvectormodel.hpp \
|
||||||
|
src/managers/commands/command.hpp
|
||||||
|
|
||||||
RESOURCES += \
|
RESOURCES += \
|
||||||
resources/resources.qrc
|
resources/resources.qrc
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 94edfacf14728faf3aa1d9c058e89395c97aae14
|
Subproject commit ad31b38866d80a17ced902476ed06da69edce3a0
|
|
@ -93,7 +93,7 @@ void Application::initialize()
|
||||||
this->nativeMessaging->registerHost();
|
this->nativeMessaging->registerHost();
|
||||||
|
|
||||||
this->settings->load();
|
this->settings->load();
|
||||||
this->commands->loadCommands();
|
this->commands->load();
|
||||||
|
|
||||||
this->emotes->loadGlobalEmotes();
|
this->emotes->loadGlobalEmotes();
|
||||||
|
|
||||||
|
@ -218,7 +218,7 @@ void Application::save()
|
||||||
{
|
{
|
||||||
this->windows->save();
|
this->windows->save();
|
||||||
|
|
||||||
this->commands->saveCommands();
|
this->commands->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::runNativeMessagingHost()
|
void Application::runNativeMessagingHost()
|
||||||
|
|
0
src/managers/commands/command.cpp
Normal file
0
src/managers/commands/command.cpp
Normal file
9
src/managers/commands/command.hpp
Normal file
9
src/managers/commands/command.hpp
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
namespace managers {
|
||||||
|
namespace commands {
|
||||||
|
// code
|
||||||
|
}
|
||||||
|
} // namespace managers
|
||||||
|
} // namespace chatterino
|
|
@ -74,8 +74,7 @@ MessagePtr TwitchMessageBuilder::build()
|
||||||
#ifdef XD
|
#ifdef XD
|
||||||
if (this->originalMessage.length() > 100) {
|
if (this->originalMessage.length() > 100) {
|
||||||
this->message->flags |= Message::Collapsed;
|
this->message->flags |= Message::Collapsed;
|
||||||
this->emplace<EmoteElement>(singletons::ResourceManager::getInstance().badgeCollapsed,
|
this->emplace<EmoteElement>(getApp()->resources->badgeCollapsed, MessageElement::Collapsed);
|
||||||
MessageElement::Collapsed);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,24 @@ using namespace chatterino::providers::twitch;
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
namespace singletons {
|
namespace singletons {
|
||||||
|
|
||||||
void CommandManager::loadCommands()
|
CommandManager::CommandManager()
|
||||||
|
{
|
||||||
|
auto addFirstMatchToMap = [this](auto args) {
|
||||||
|
this->commandsMap.remove(args.item.name);
|
||||||
|
|
||||||
|
for (const Command &cmd : this->commands.getVector()) {
|
||||||
|
if (cmd.name == args.item.name) {
|
||||||
|
this->commandsMap[cmd.name] = cmd;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this->commands.itemInserted.connect(addFirstMatchToMap);
|
||||||
|
this->commands.itemRemoved.connect(addFirstMatchToMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandManager::load()
|
||||||
{
|
{
|
||||||
auto app = getApp();
|
auto app = getApp();
|
||||||
this->filePath = app->paths->customFolderPath + "/Commands.txt";
|
this->filePath = app->paths->customFolderPath + "/Commands.txt";
|
||||||
|
@ -32,18 +49,14 @@ void CommandManager::loadCommands()
|
||||||
|
|
||||||
QList<QByteArray> test = textFile.readAll().split('\n');
|
QList<QByteArray> test = textFile.readAll().split('\n');
|
||||||
|
|
||||||
QStringList loadedCommands;
|
|
||||||
|
|
||||||
for (const auto &command : test) {
|
for (const auto &command : test) {
|
||||||
loadedCommands.append(command);
|
this->commands.appendItem(Command(command));
|
||||||
}
|
}
|
||||||
|
|
||||||
this->setCommands(loadedCommands);
|
|
||||||
|
|
||||||
textFile.close();
|
textFile.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandManager::saveCommands()
|
void CommandManager::save()
|
||||||
{
|
{
|
||||||
QFile textFile(this->filePath);
|
QFile textFile(this->filePath);
|
||||||
if (!textFile.open(QIODevice::WriteOnly)) {
|
if (!textFile.open(QIODevice::WriteOnly)) {
|
||||||
|
@ -51,44 +64,16 @@ void CommandManager::saveCommands()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString commandsString = this->commandsStringList.join('\n');
|
for (const Command &cmd : this->commands.getVector()) {
|
||||||
|
textFile.write((cmd.toString() + "\n").toUtf8());
|
||||||
textFile.write(commandsString.toUtf8());
|
}
|
||||||
|
|
||||||
textFile.close();
|
textFile.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandManager::setCommands(const QStringList &_commands)
|
CommandModel *CommandManager::createModel(QObject *parent)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(this->mutex);
|
return new CommandModel(&this->commands, parent);
|
||||||
|
|
||||||
this->commands.clear();
|
|
||||||
|
|
||||||
for (const QString &commandRef : _commands) {
|
|
||||||
QString command = commandRef;
|
|
||||||
|
|
||||||
if (command.size() == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (command.at(0) != '/') {
|
|
||||||
// command = QString("/") + command;
|
|
||||||
// }
|
|
||||||
|
|
||||||
QString commandName = command.mid(0, command.indexOf(' '));
|
|
||||||
|
|
||||||
if (this->commands.find(commandName) == this->commands.end()) {
|
|
||||||
this->commands.insert(commandName, Command(command));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this->commandsStringList = _commands;
|
|
||||||
this->commandsStringList.detach();
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList CommandManager::getCommands()
|
|
||||||
{
|
|
||||||
return this->commandsStringList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CommandManager::execCommand(const QString &text, ChannelPtr channel, bool dryRun)
|
QString CommandManager::execCommand(const QString &text, ChannelPtr channel, bool dryRun)
|
||||||
|
@ -173,9 +158,8 @@ QString CommandManager::execCommand(const QString &text, ChannelPtr channel, boo
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if custom command exists
|
// check if custom command exists
|
||||||
auto it = this->commands.find(commandName);
|
auto it = this->commandsMap.find(commandName);
|
||||||
|
if (it == this->commandsMap.end()) {
|
||||||
if (it == this->commands.end()) {
|
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,17 +177,17 @@ QString CommandManager::execCustomCommand(const QStringList &words, const Comman
|
||||||
|
|
||||||
int lastCaptureEnd = 0;
|
int lastCaptureEnd = 0;
|
||||||
|
|
||||||
auto globalMatch = parseCommand.globalMatch(command.text);
|
auto globalMatch = parseCommand.globalMatch(command.func);
|
||||||
int matchOffset = 0;
|
int matchOffset = 0;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
QRegularExpressionMatch match = parseCommand.match(command.text, matchOffset);
|
QRegularExpressionMatch match = parseCommand.match(command.func, matchOffset);
|
||||||
|
|
||||||
if (!match.hasMatch()) {
|
if (!match.hasMatch()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
result += command.text.mid(lastCaptureEnd, match.capturedStart() - lastCaptureEnd + 1);
|
result += command.func.mid(lastCaptureEnd, match.capturedStart() - lastCaptureEnd + 1);
|
||||||
|
|
||||||
lastCaptureEnd = match.capturedEnd();
|
lastCaptureEnd = match.capturedEnd();
|
||||||
matchOffset = lastCaptureEnd - 1;
|
matchOffset = lastCaptureEnd - 1;
|
||||||
|
@ -238,7 +222,7 @@ QString CommandManager::execCustomCommand(const QStringList &words, const Comman
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result += command.text.mid(lastCaptureEnd);
|
result += command.func.mid(lastCaptureEnd);
|
||||||
|
|
||||||
if (result.size() > 0 && result.at(0) == '{') {
|
if (result.size() > 0 && result.at(0) == '{') {
|
||||||
result = result.mid(1);
|
result = result.mid(1);
|
||||||
|
@ -247,7 +231,30 @@ QString CommandManager::execCustomCommand(const QStringList &words, const Comman
|
||||||
return result.replace("{{", "{");
|
return result.replace("{{", "{");
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandManager::Command::Command(QString _text)
|
// commandmodel
|
||||||
|
CommandModel::CommandModel(util::BaseSignalVector<Command> *vec, QObject *parent)
|
||||||
|
: util::SignalVectorModel<Command>(vec, 2, parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int CommandModel::prepareInsert(const Command &item, int index,
|
||||||
|
std::vector<QStandardItem *> &rowToAdd)
|
||||||
|
{
|
||||||
|
rowToAdd[0]->setData(item.name, Qt::EditRole);
|
||||||
|
rowToAdd[1]->setData(item.func, Qt::EditRole);
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CommandModel::prepareRemove(const Command &item, int index)
|
||||||
|
{
|
||||||
|
UNUSED(item);
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// command
|
||||||
|
Command::Command(const QString &_text)
|
||||||
{
|
{
|
||||||
int index = _text.indexOf(' ');
|
int index = _text.indexOf(' ');
|
||||||
|
|
||||||
|
@ -257,7 +264,18 @@ CommandManager::Command::Command(QString _text)
|
||||||
}
|
}
|
||||||
|
|
||||||
this->name = _text.mid(0, index);
|
this->name = _text.mid(0, index);
|
||||||
this->text = _text.mid(index + 1);
|
this->func = _text.mid(index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Command::Command(const QString &_name, const QString &_func)
|
||||||
|
: name(_name)
|
||||||
|
, func(_func)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Command::toString() const
|
||||||
|
{
|
||||||
|
return this->name + " " + this->func;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace singletons
|
} // namespace singletons
|
||||||
|
|
|
@ -6,40 +6,60 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <util/signalvector2.hpp>
|
||||||
|
#include <util/signalvectormodel.hpp>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
class Channel;
|
class Channel;
|
||||||
|
|
||||||
namespace singletons {
|
namespace singletons {
|
||||||
|
|
||||||
|
class CommandManager;
|
||||||
|
|
||||||
|
struct Command {
|
||||||
|
QString name;
|
||||||
|
QString func;
|
||||||
|
|
||||||
|
Command() = default;
|
||||||
|
explicit Command(const QString &text);
|
||||||
|
Command(const QString &name, const QString &func);
|
||||||
|
|
||||||
|
QString toString() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CommandModel : public util::SignalVectorModel<Command>
|
||||||
|
{
|
||||||
|
explicit CommandModel(util::BaseSignalVector<Command> *vec, QObject *parent);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual int prepareInsert(const Command &item, int index,
|
||||||
|
std::vector<QStandardItem *> &rowToAdd) override;
|
||||||
|
virtual int prepareRemove(const Command &item, int index) override;
|
||||||
|
|
||||||
|
friend class CommandManager;
|
||||||
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// this class managed the custom /commands
|
// this class managed the custom /commands
|
||||||
//
|
//
|
||||||
|
|
||||||
class CommandManager
|
class CommandManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CommandManager() = default;
|
CommandManager();
|
||||||
|
|
||||||
QString execCommand(const QString &text, std::shared_ptr<Channel> channel, bool dryRun);
|
QString execCommand(const QString &text, std::shared_ptr<Channel> channel, bool dryRun);
|
||||||
|
|
||||||
void loadCommands();
|
void load();
|
||||||
void saveCommands();
|
void save();
|
||||||
|
|
||||||
void setCommands(const QStringList &commands);
|
CommandModel *createModel(QObject *parent);
|
||||||
QStringList getCommands();
|
|
||||||
|
util::UnsortedSignalVector<Command> commands;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Command {
|
QMap<QString, Command> commandsMap;
|
||||||
QString name;
|
|
||||||
QString text;
|
|
||||||
|
|
||||||
Command() = default;
|
|
||||||
Command(QString text);
|
|
||||||
};
|
|
||||||
|
|
||||||
QMap<QString, Command> commands;
|
|
||||||
std::mutex mutex;
|
std::mutex mutex;
|
||||||
QStringList commandsStringList;
|
|
||||||
QString filePath;
|
QString filePath;
|
||||||
|
|
||||||
QString execCustomCommand(const QStringList &words, const Command &command);
|
QString execCustomCommand(const QStringList &words, const Command &command);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
namespace util {
|
namespace util {
|
||||||
|
|
||||||
void assertInGuiThread()
|
static void assertInGuiThread()
|
||||||
{
|
{
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
assert(QCoreApplication::instance()->thread() == QThread::currentThread());
|
assert(QCoreApplication::instance()->thread() == QThread::currentThread());
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QStandardItemModel>
|
#include <QStandardItemModel>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
#include <pajlada/signals/signal.hpp>
|
#include <pajlada/signals/signal.hpp>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -10,18 +12,24 @@ namespace chatterino {
|
||||||
namespace util {
|
namespace util {
|
||||||
|
|
||||||
template <typename TVectorItem>
|
template <typename TVectorItem>
|
||||||
class ReadOnlySignalVector
|
class ReadOnlySignalVector : boost::noncopyable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
ReadOnlySignalVector()
|
||||||
|
{
|
||||||
|
QObject::connect(&this->itemsChangedTimer, &QTimer::timeout,
|
||||||
|
[this] { this->delayedItemsChanged.invoke(); });
|
||||||
|
}
|
||||||
virtual ~ReadOnlySignalVector() = default;
|
virtual ~ReadOnlySignalVector() = default;
|
||||||
|
|
||||||
struct ItemInsertedArgs {
|
struct ItemArgs {
|
||||||
const TVectorItem &item;
|
const TVectorItem &item;
|
||||||
int index;
|
int index;
|
||||||
|
void *caller;
|
||||||
};
|
};
|
||||||
|
|
||||||
pajlada::Signals::Signal<ItemInsertedArgs> itemInserted;
|
pajlada::Signals::Signal<ItemArgs> itemInserted;
|
||||||
pajlada::Signals::Signal<int> itemRemoved;
|
pajlada::Signals::Signal<ItemArgs> itemRemoved;
|
||||||
pajlada::Signals::NoArgSignal delayedItemsChanged;
|
pajlada::Signals::NoArgSignal delayedItemsChanged;
|
||||||
|
|
||||||
const std::vector<TVectorItem> &getVector() const
|
const std::vector<TVectorItem> &getVector() const
|
||||||
|
@ -31,42 +39,56 @@ public:
|
||||||
return this->vector;
|
return this->vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void invokeDelayedItemsChanged()
|
||||||
|
{
|
||||||
|
util::assertInGuiThread();
|
||||||
|
|
||||||
|
if (!this->itemsChangedTimer.isActive()) {
|
||||||
|
itemsChangedTimer.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::vector<TVectorItem> vector;
|
std::vector<TVectorItem> vector;
|
||||||
|
QTimer itemsChangedTimer;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename TVectorItem>
|
template <typename TVectorItem>
|
||||||
class BaseSignalVector : public ReadOnlySignalVector<TVectorItem>
|
class BaseSignalVector : public ReadOnlySignalVector<TVectorItem>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void removeItem(int index)
|
virtual void appendItem(const TVectorItem &item, void *caller = 0) = 0;
|
||||||
|
|
||||||
|
void removeItem(int index, void *caller = 0)
|
||||||
{
|
{
|
||||||
util::assertInGuiThread();
|
util::assertInGuiThread();
|
||||||
assert(index >= 0 && index < this->vector.size());
|
assert(index >= 0 && index < this->vector.size());
|
||||||
|
|
||||||
|
TVectorItem item = this->vector[index];
|
||||||
this->vector.erase(this->vector.begin() + index);
|
this->vector.erase(this->vector.begin() + index);
|
||||||
this->itemRemoved.invoke(index);
|
ItemArgs args{item, args, caller};
|
||||||
|
this->itemRemoved.invoke(args);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename TVectorItem>
|
template <typename TVectorItem>
|
||||||
class SignalVector2 : public BaseSignalVector<TVectorItem>
|
class UnsortedSignalVector : public BaseSignalVector<TVectorItem>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void insertItem(const TVectorItem &item, int index)
|
void insertItem(const TVectorItem &item, int index, void *caller = 0)
|
||||||
{
|
{
|
||||||
util::assertInGuiThread();
|
util::assertInGuiThread();
|
||||||
assert(index >= 0 && index <= this->vector.size());
|
assert(index >= 0 && index <= this->vector.size());
|
||||||
|
|
||||||
this->vector.insert(this->vector.begin() + index, item);
|
this->vector.insert(this->vector.begin() + index, item);
|
||||||
|
|
||||||
ItemInsertedArgs args{item, index};
|
ItemArgs args{item, index, caller};
|
||||||
this->itemInserted.invoke(args);
|
this->itemInserted.invoke(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
void appendItem(const TVectorItem &item)
|
virtual void appendItem(const TVectorItem &item, void *caller = 0) override
|
||||||
{
|
{
|
||||||
this->insertItem(item, this->vector.size());
|
this->insertItem(item, this->vector.size(), caller);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -74,14 +96,14 @@ template <typename TVectorItem>
|
||||||
class SortedSignalVector : public BaseSignalVector<TVectorItem>
|
class SortedSignalVector : public BaseSignalVector<TVectorItem>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void addItem(const TVectorItem &item)
|
virtual void appendItem(const TVectorItem &item, void *caller = 0) override
|
||||||
{
|
{
|
||||||
util::assertInGuiThread();
|
util::assertInGuiThread();
|
||||||
|
|
||||||
int index = this->vector.insert(
|
int index = this->vector.insert(
|
||||||
std::lower_bound(this->vector.begin(), this->vector.end(), item), item) -
|
std::lower_bound(this->vector.begin(), this->vector.end(), item), item) -
|
||||||
this->vector.begin();
|
this->vector.begin();
|
||||||
ItemInsertedArgs args{item, index};
|
ItemArgs args{item, index, caller};
|
||||||
this->itemInserted.invoke(args);
|
this->itemInserted.invoke(args);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
1
src/util/signalvectormodel.cpp
Normal file
1
src/util/signalvectormodel.cpp
Normal file
|
@ -0,0 +1 @@
|
||||||
|
#include "signalvectormodel.hpp"
|
112
src/util/signalvectormodel.hpp
Normal file
112
src/util/signalvectormodel.hpp
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QAbstractTableModel>
|
||||||
|
#include <QStandardItem>
|
||||||
|
#include <util/signalvector2.hpp>
|
||||||
|
|
||||||
|
#include <pajlada/signals/signalholder.hpp>
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
namespace util {
|
||||||
|
|
||||||
|
template <typename TVectorItem>
|
||||||
|
class SignalVectorModel : public QAbstractTableModel, pajlada::Signals::SignalHolder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SignalVectorModel(util::BaseSignalVector<TVectorItem> *vec, int columnCount,
|
||||||
|
QObject *parent = nullptr)
|
||||||
|
: QAbstractTableModel(parent)
|
||||||
|
, _columnCount(columnCount)
|
||||||
|
{
|
||||||
|
this->managedConnect(vec->itemInserted, [this](auto args) {
|
||||||
|
std::vector<QStandardItem *> items;
|
||||||
|
for (int i = 0; i < this->_columnCount; i++) {
|
||||||
|
items.push_back(new QStandardItem());
|
||||||
|
}
|
||||||
|
|
||||||
|
int row = this->prepareInsert(args.item, args.index, items);
|
||||||
|
assert(row >= 0 && row <= this->rows.size());
|
||||||
|
|
||||||
|
// insert row
|
||||||
|
this->beginInsertRows(QModelIndex(), row, row);
|
||||||
|
this->rows.insert(this->rows.begin() + row, Row(items));
|
||||||
|
this->endInsertRows();
|
||||||
|
});
|
||||||
|
this->managedConnect(vec->itemRemoved, [this](auto args) {
|
||||||
|
int row = this->prepareRemove(args.item, args.index);
|
||||||
|
assert(row >= 0 && row <= this->rows.size());
|
||||||
|
|
||||||
|
// remove row
|
||||||
|
this->beginRemoveRows(QModelIndex(), row, row);
|
||||||
|
for (QStandardItem *item : this->rows[row].items) {
|
||||||
|
delete item;
|
||||||
|
}
|
||||||
|
this->rows.erase(this->rows.begin() + row);
|
||||||
|
this->endRemoveRows();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~SignalVectorModel()
|
||||||
|
{
|
||||||
|
for (Row &row : this->rows) {
|
||||||
|
for (QStandardItem *item : row.items) {
|
||||||
|
delete item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
return this->rows.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int columnCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
return this->_columnCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
int row = index.row(), column = index.column();
|
||||||
|
assert(row >= 0 && row < this->rows.size() && column >= 0 && column < this->_columnCount);
|
||||||
|
|
||||||
|
return rows[row].items[column]->data(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool setData(const QModelIndex &index, const QVariant &value, int role)
|
||||||
|
{
|
||||||
|
this->rows[index.row()].items[index.column()]->setData(value, role);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStandardItem *getItem(int row, int column)
|
||||||
|
{
|
||||||
|
assert(row >= 0 && row < this->rows.size() && column >= 0 && column < this->_columnCount);
|
||||||
|
|
||||||
|
return rows[row][column];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual int prepareInsert(const TVectorItem &item, int index,
|
||||||
|
std::vector<QStandardItem *> &rowToAdd) = 0;
|
||||||
|
virtual int prepareRemove(const TVectorItem &item, int index) = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Row {
|
||||||
|
std::vector<QStandardItem *> items;
|
||||||
|
bool isCustomRow;
|
||||||
|
|
||||||
|
Row(const std::vector<QStandardItem *> _items, bool _isCustomRow = false)
|
||||||
|
: items(_items)
|
||||||
|
, isCustomRow(_isCustomRow)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<Row> rows;
|
||||||
|
int _columnCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace util
|
||||||
|
} // namespace chatterino
|
|
@ -34,68 +34,73 @@ CommandPage::CommandPage()
|
||||||
auto layout = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
|
auto layout = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
|
||||||
|
|
||||||
QTableView *view = *layout.emplace<QTableView>();
|
QTableView *view = *layout.emplace<QTableView>();
|
||||||
QStandardItemModel *model = new QStandardItemModel(0, 2, view);
|
|
||||||
|
|
||||||
view->setModel(model);
|
auto *model = app->commands->createModel(this);
|
||||||
model->setHeaderData(0, Qt::Horizontal, "Trigger");
|
|
||||||
model->setHeaderData(1, Qt::Horizontal, "Command");
|
|
||||||
view->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
|
||||||
view->setSelectionBehavior(QAbstractItemView::SelectRows);
|
|
||||||
view->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
|
|
||||||
|
|
||||||
for (const QString &string : app->commands->getCommands()) {
|
// QTableView *view = *layout.emplace<QTableView>();
|
||||||
int index = string.indexOf(' ');
|
// QStandardItemModel *model = new QStandardItemModel(0, 2, view);
|
||||||
if (index == -1) {
|
|
||||||
model->appendRow({util::stringItem(string), util::stringItem("")});
|
|
||||||
} else {
|
|
||||||
model->appendRow(
|
|
||||||
{util::stringItem(string.mid(0, index)), util::stringItem(string.mid(index + 1))});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QObject::connect(
|
// view->setModel(model);
|
||||||
model, &QStandardItemModel::dataChanged,
|
// model->setHeaderData(0, Qt::Horizontal, "Trigger");
|
||||||
[model](const QModelIndex &topLeft, const QModelIndex &bottomRight,
|
// model->setHeaderData(1, Qt::Horizontal, "Command");
|
||||||
const QVector<int> &roles) {
|
// view->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||||
QStringList list;
|
// view->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||||
|
// view->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
|
||||||
|
|
||||||
for (int i = 0; i < model->rowCount(); i++) {
|
// for (const QString &string : app->commands->getCommands()) {
|
||||||
QString command = model->item(i, 0)->data(Qt::EditRole).toString();
|
// int index = string.indexOf(' ');
|
||||||
// int index = command.indexOf(' ');
|
// if (index == -1) {
|
||||||
// if (index != -1) {
|
// model->appendRow({util::stringItem(string), util::stringItem("")});
|
||||||
// command = command.mid(index);
|
// } else {
|
||||||
|
// model->appendRow(
|
||||||
|
// {util::stringItem(string.mid(0, index)), util::stringItem(string.mid(index +
|
||||||
|
// 1))});
|
||||||
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
list.append(command + " " + model->item(i, 1)->data(Qt::EditRole).toString());
|
// QObject::connect(
|
||||||
}
|
// model, &QStandardItemModel::dataChanged,
|
||||||
|
// [model](const QModelIndex &topLeft, const QModelIndex &bottomRight,
|
||||||
|
// const QVector<int> &roles) {
|
||||||
|
// QStringList list;
|
||||||
|
|
||||||
getApp()->commands->setCommands(list);
|
// for (int i = 0; i < model->rowCount(); i++) {
|
||||||
});
|
// QString command = model->item(i, 0)->data(Qt::EditRole).toString();
|
||||||
|
// // int index = command.indexOf(' ');
|
||||||
|
// // if (index != -1) {
|
||||||
|
// // command = command.mid(index);
|
||||||
|
// // }
|
||||||
|
|
||||||
auto buttons = layout.emplace<QHBoxLayout>().withoutMargin();
|
// list.append(command + " " + model->item(i, 1)->data(Qt::EditRole).toString());
|
||||||
{
|
// }
|
||||||
auto add = buttons.emplace<QPushButton>("Add");
|
|
||||||
QObject::connect(*add, &QPushButton::clicked, [model, view] {
|
|
||||||
model->appendRow({util::stringItem("/command"), util::stringItem("")});
|
|
||||||
view->scrollToBottom();
|
|
||||||
});
|
|
||||||
|
|
||||||
auto remove = buttons.emplace<QPushButton>("Remove");
|
// getApp()->commands->setCommands(list);
|
||||||
QObject::connect(*remove, &QPushButton::clicked, [view, model] {
|
// });
|
||||||
std::vector<int> indices;
|
|
||||||
|
|
||||||
for (const QModelIndex &index : view->selectionModel()->selectedRows(0)) {
|
// auto buttons = layout.emplace<QHBoxLayout>().withoutMargin();
|
||||||
indices.push_back(index.row());
|
// {
|
||||||
}
|
// auto add = buttons.emplace<QPushButton>("Add");
|
||||||
|
// QObject::connect(*add, &QPushButton::clicked, [model, view] {
|
||||||
|
// model->appendRow({util::stringItem("/command"), util::stringItem("")});
|
||||||
|
// view->scrollToBottom();
|
||||||
|
// });
|
||||||
|
|
||||||
std::sort(indices.begin(), indices.end());
|
// auto remove = buttons.emplace<QPushButton>("Remove");
|
||||||
|
// QObject::connect(*remove, &QPushButton::clicked, [view, model] {
|
||||||
|
// std::vector<int> indices;
|
||||||
|
|
||||||
for (int i = indices.size() - 1; i >= 0; i--) {
|
// for (const QModelIndex &index : view->selectionModel()->selectedRows(0)) {
|
||||||
model->removeRow(indices[i]);
|
// indices.push_back(index.row());
|
||||||
}
|
// }
|
||||||
});
|
|
||||||
buttons->addStretch(1);
|
// std::sort(indices.begin(), indices.end());
|
||||||
}
|
|
||||||
|
// for (int i = indices.size() - 1; i >= 0; i--) {
|
||||||
|
// model->removeRow(indices[i]);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// buttons->addStretch(1);
|
||||||
|
// }
|
||||||
|
|
||||||
layout.append(this->createCheckBox("Also match the trigger at the end of the message",
|
layout.append(this->createCheckBox("Also match the trigger at the end of the message",
|
||||||
app->settings->allowCommandsAtEnd));
|
app->settings->allowCommandsAtEnd));
|
||||||
|
@ -108,33 +113,6 @@ CommandPage::CommandPage()
|
||||||
this->commandsEditTimer.setSingleShot(true);
|
this->commandsEditTimer.setSingleShot(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
QTextEdit *CommandPage::getCommandsTextEdit()
|
|
||||||
{
|
|
||||||
auto app = getApp();
|
|
||||||
|
|
||||||
// cancel
|
|
||||||
QStringList currentCommands = app->commands->getCommands();
|
|
||||||
|
|
||||||
this->onCancel.connect([currentCommands, app] { app->commands->setCommands(currentCommands); });
|
|
||||||
|
|
||||||
// create text edit
|
|
||||||
QTextEdit *textEdit = new QTextEdit;
|
|
||||||
|
|
||||||
textEdit->setPlainText(QString(app->commands->getCommands().join('\n')));
|
|
||||||
|
|
||||||
QObject::connect(textEdit, &QTextEdit::textChanged,
|
|
||||||
[this] { this->commandsEditTimer.start(200); });
|
|
||||||
|
|
||||||
QObject::connect(&this->commandsEditTimer, &QTimer::timeout, [textEdit, app] {
|
|
||||||
QString text = textEdit->toPlainText();
|
|
||||||
QStringList lines = text.split(QRegularExpression("(\r?\n|\r\n?)"));
|
|
||||||
|
|
||||||
app->commands->setCommands(lines);
|
|
||||||
});
|
|
||||||
|
|
||||||
return textEdit;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace settingspages
|
} // namespace settingspages
|
||||||
} // namespace widgets
|
} // namespace widgets
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -15,8 +15,6 @@ public:
|
||||||
CommandPage();
|
CommandPage();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QTextEdit *getCommandsTextEdit();
|
|
||||||
|
|
||||||
QTimer commandsEditTimer;
|
QTimer commandsEditTimer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue