Skip to content

Commit

Permalink
Feature: execute commands on chat messages using the context menu (Ch…
Browse files Browse the repository at this point in the history
  • Loading branch information
xel86 authored May 22, 2022
1 parent 7d9c3c6 commit 4239666
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- Minor: Prevent user from entering incorrect characters in Live Notifications channels list. (#3715, #3730)
- Minor: Fixed automod caught message notice appearing twice for mods. (#3717)
- Minor: Added `/requests` command. Usage: `/requests [channel]`. Opens the channel points requests queue for the provided channel or the current channel if no input is provided. (#3746)
- Minor: Added ability to execute commands on chat messages using the message context menu. (#3738)
- Bugfix: Fixed live notifications for usernames containing uppercase characters. (#3646)
- Bugfix: Fixed live notifications not getting updated for closed streams going offline. (#3678)
- Bugfix: Fixed certain settings dialogs appearing behind the main window, when `Always on top` was used. (#3679)
Expand Down
5 changes: 4 additions & 1 deletion src/controllers/commands/Command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ Command::Command(const QString &_text)

this->name = _text.mid(0, index).trimmed();
this->func = _text.mid(index + 1).trimmed();
this->showInMsgContextMenu = false;
}

Command::Command(const QString &_name, const QString &_func)
Command::Command(const QString &_name, const QString &_func,
bool _showInMsgContextMenu)
: name(_name.trimmed())
, func(_func.trimmed())
, showInMsgContextMenu(_showInMsgContextMenu)
{
}

Expand Down
15 changes: 14 additions & 1 deletion src/controllers/commands/Command.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ namespace chatterino {
struct Command {
QString name;
QString func;
bool showInMsgContextMenu;

Command() = default;
explicit Command(const QString &text);
Command(const QString &name, const QString &func);
Command(const QString &name, const QString &func,
bool showInMsgContextMenu = false);

QString toString() const;
};
Expand All @@ -31,6 +33,8 @@ struct Serialize<chatterino::Command> {

chatterino::rj::set(ret, "name", value.name, a);
chatterino::rj::set(ret, "func", value.func, a);
chatterino::rj::set(ret, "showInMsgContextMenu",
value.showInMsgContextMenu, a);

return ret;
}
Expand Down Expand Up @@ -59,6 +63,15 @@ struct Deserialize<chatterino::Command> {
PAJLADA_REPORT_ERROR(error);
return command;
}
if (!chatterino::rj::getSafe(value, "showInMsgContextMenu",
command.showInMsgContextMenu))
{
command.showInMsgContextMenu = false;

PAJLADA_REPORT_ERROR(error);

return command;
}

return command;
}
Expand Down
15 changes: 10 additions & 5 deletions src/controllers/commands/CommandModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,29 @@ namespace chatterino {

// commandmodel
CommandModel::CommandModel(QObject *parent)
: SignalVectorModel<Command>(2, parent)
: SignalVectorModel<Command>(Column::COUNT, parent)
{
}

// turn a vector item into a model row
Command CommandModel::getItemFromRow(std::vector<QStandardItem *> &row,
const Command &original)
{
return Command(row[0]->data(Qt::EditRole).toString(),
row[1]->data(Qt::EditRole).toString());
return Command(row[Column::Trigger]->data(Qt::EditRole).toString(),
row[Column::CommandFunc]->data(Qt::EditRole).toString(),
row[Column::ShowInMessageContextMenu]
->data(Qt::CheckStateRole)
.toBool());
}

// turns a row in the model into a vector item
void CommandModel::getRowFromItem(const Command &item,
std::vector<QStandardItem *> &row)
{
setStringItem(row[0], item.name);
setStringItem(row[1], item.func);
setStringItem(row[Column::Trigger], item.name);
setStringItem(row[Column::CommandFunc], item.func);
setBoolItem(row[Column::ShowInMessageContextMenu],
item.showInMsgContextMenu);
}

} // namespace chatterino
7 changes: 7 additions & 0 deletions src/controllers/commands/CommandModel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ class CommandModel : public SignalVectorModel<Command>
{
explicit CommandModel(QObject *parent);

enum Column {
Trigger = 0,
CommandFunc = 1,
ShowInMessageContextMenu = 2,
COUNT,
};

protected:
// turn a vector item into a model row
virtual Command getItemFromRow(std::vector<QStandardItem *> &row,
Expand Down
60 changes: 60 additions & 0 deletions src/widgets/helper/ChannelView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1885,6 +1885,10 @@ void ChannelView::addContextMenuItems(
// Add hidden options (e.g. copy message ID) if the user held down Shift
this->addHiddenContextMenuItems(hoveredElement, layout, event, *menu);

// Add executable command options
this->addCommandExecutionContextMenuItems(hoveredElement, layout, event,
*menu);

menu->popup(QCursor::pos());
menu->raise();
}
Expand Down Expand Up @@ -2081,6 +2085,62 @@ void ChannelView::addHiddenContextMenuItems(
});
}
}

void ChannelView::addCommandExecutionContextMenuItems(
const MessageLayoutElement * /*hoveredElement*/, MessageLayoutPtr layout,
QMouseEvent * /*event*/, QMenu &menu)
{
/* Get commands to be displayed in context menu;
* only those that had the showInMsgContextMenu check box marked in the Commands page */
std::vector<Command> cmds;
for (auto &cmd : getApp()->commands->items)
{
if (cmd.showInMsgContextMenu)
{
cmds.push_back(cmd);
}
}

if (cmds.empty())
{
return;
}

menu.addSeparator();
auto executeAction = menu.addAction("Execute command");
auto cmdMenu = new QMenu;
executeAction->setMenu(cmdMenu);

for (auto &cmd : cmds)
{
QString inputText = this->selection_.isEmpty()
? layout->getMessage()->messageText
: this->getSelectedText();

inputText.push_front(cmd.name + " ");

cmdMenu->addAction(cmd.name, [this, inputText] {
ChannelPtr channel;

/* Search popups and user message history's underlyingChannels aren't of type TwitchChannel, but
* we would still like to execute commands from them. Use their source channel instead if applicable. */
if (this->hasSourceChannel())
{
channel = this->sourceChannel();
}
else
{
channel = this->underlyingChannel_;
}

QString value =
getApp()->commands->execCommand(inputText, channel, false);

channel->sendMessage(value);
});
}
}

void ChannelView::mouseDoubleClickEvent(QMouseEvent *event)
{
if (event->button() != Qt::LeftButton)
Expand Down
4 changes: 4 additions & 0 deletions src/widgets/helper/ChannelView.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,10 @@ class ChannelView final : public BaseWidget
void addHiddenContextMenuItems(const MessageLayoutElement *hoveredElement,
MessageLayoutPtr layout, QMouseEvent *event,
QMenu &menu);
void addCommandExecutionContextMenuItems(
const MessageLayoutElement *hoveredElement, MessageLayoutPtr layout,
QMouseEvent *event, QMenu &menu);

int getLayoutWidth() const;
void updatePauses();
void unpaused();
Expand Down
5 changes: 3 additions & 2 deletions src/widgets/settingspages/CommandPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ CommandPage::CommandPage()
layout.emplace<EditableModelView>(app->commands->createModel(nullptr))
.getElement();

view->setTitles({"Trigger", "Command"});
view->getTableView()->horizontalHeader()->setStretchLastSection(true);
view->setTitles({"Trigger", "Command", "Show In\nMessage Menu"});
view->getTableView()->horizontalHeader()->setSectionResizeMode(
1, QHeaderView::Stretch);
view->addButtonPressed.connect([] {
getApp()->commands->items.append(
Command{"/command", "I made a new command HeyGuys"});
Expand Down

0 comments on commit 4239666

Please sign in to comment.