Add custom hotkeys. (#2340)

Co-authored-by: LosFarmosCTL <80157503+LosFarmosCTL@users.noreply.github.com>
Co-authored-by: Paweł <zneix@zneix.eu>
Co-authored-by: Felanbird <41973452+Felanbird@users.noreply.github.com>
Co-authored-by: Rasmus Karlsson <rasmus.karlsson@pajlada.com>
This commit is contained in:
Mm2PL
2021-11-21 17:46:21 +00:00
committed by GitHub
parent b94e21a600
commit 703f3717e2
54 changed files with 3613 additions and 617 deletions
@@ -106,6 +106,10 @@ ColorPickerDialog::ColorPickerDialog(const QColor &initial, QWidget *parent)
this->selectColor(initial, false);
}
void ColorPickerDialog::addShortcuts()
{
}
ColorPickerDialog::~ColorPickerDialog()
{
if (this->htmlColorValidator_)
@@ -108,5 +108,7 @@ private:
void initColorPicker(LayoutCreator<QWidget> &creator);
void initSpinBoxes(LayoutCreator<QWidget> &creator);
void initHtmlColor(LayoutCreator<QWidget> &creator);
void addShortcuts() override;
};
} // namespace chatterino
+312
View File
@@ -0,0 +1,312 @@
#include "widgets/dialogs/EditHotkeyDialog.hpp"
#include "Application.hpp"
#include "common/QLogging.hpp"
#include "controllers/hotkeys/ActionNames.hpp"
#include "controllers/hotkeys/HotkeyController.hpp"
#include "controllers/hotkeys/HotkeyHelpers.hpp"
#include "ui_EditHotkeyDialog.h"
namespace chatterino {
EditHotkeyDialog::EditHotkeyDialog(const std::shared_ptr<Hotkey> hotkey,
bool isAdd, QWidget *parent)
: QDialog(parent, Qt::WindowStaysOnTopHint)
, ui_(new Ui::EditHotkeyDialog)
, data_(hotkey)
{
this->ui_->setupUi(this);
// dynamically add category names to the category picker
for (const auto &[_, hotkeyCategory] : getApp()->hotkeys->categories())
{
this->ui_->categoryPicker->addItem(hotkeyCategory.displayName,
hotkeyCategory.name);
}
this->ui_->warningLabel->hide();
if (hotkey)
{
if (!hotkey->validAction())
{
this->showEditError("Invalid action, make sure you select the "
"correct action before saving.");
}
// editing a hotkey
// update pickers/input boxes to values from Hotkey object
this->ui_->categoryPicker->setCurrentIndex(size_t(hotkey->category()));
this->ui_->keyComboEdit->setKeySequence(
QKeySequence::fromString(hotkey->keySequence().toString()));
this->ui_->nameEdit->setText(hotkey->name());
// update arguments
QString argsText;
bool first = true;
for (const auto &arg : hotkey->arguments())
{
if (!first)
{
argsText += '\n';
}
argsText += arg;
first = false;
}
this->ui_->argumentsEdit->setPlainText(argsText);
}
else
{
// adding a new hotkey
this->setWindowTitle("Add hotkey");
this->ui_->categoryPicker->setCurrentIndex(
size_t(HotkeyCategory::SplitInput));
this->ui_->argumentsEdit->setPlainText("");
}
}
EditHotkeyDialog::~EditHotkeyDialog()
{
delete this->ui_;
}
std::shared_ptr<Hotkey> EditHotkeyDialog::data()
{
return this->data_;
}
void EditHotkeyDialog::afterEdit()
{
auto arguments =
parseHotkeyArguments(this->ui_->argumentsEdit->toPlainText());
auto category = getApp()->hotkeys->hotkeyCategoryFromName(
this->ui_->categoryPicker->currentData().toString());
if (!category)
{
this->showEditError("Invalid Hotkey Category.");
return;
}
QString nameText = this->ui_->nameEdit->text();
// check if another hotkey with this name exists, accounts for editing a hotkey
bool isEditing = bool(this->data_);
if (getApp()->hotkeys->getHotkeyByName(nameText))
{
// A hotkey with this name already exists
if (isEditing && this->data()->name() == nameText)
{
// The hotkey that already exists is the one we are editing
}
else
{
// The user is either creating a hotkey with a name that already exists, or
// the user is editing an already-existing hotkey and changing its name to a hotkey that already exists
this->showEditError("Hotkey with this name already exists.");
return;
}
}
if (nameText.isEmpty())
{
this->showEditError("Hotkey name is missing");
return;
}
if (this->ui_->keyComboEdit->keySequence().count() == 0)
{
this->showEditError("Key Sequence is missing");
return;
}
if (this->ui_->actionPicker->currentText().isEmpty())
{
this->showEditError("Action name cannot be empty");
return;
}
auto firstKeyInt = this->ui_->keyComboEdit->keySequence()[0];
bool hasModifier = ((firstKeyInt & Qt::CTRL) == Qt::CTRL) ||
((firstKeyInt & Qt::ALT) == Qt::ALT) ||
((firstKeyInt & Qt::META) == Qt::META);
bool isKeyExcempt = ((firstKeyInt & Qt::Key_Escape) == Qt::Key_Escape) ||
((firstKeyInt & Qt::Key_Enter) == Qt::Key_Enter) ||
((firstKeyInt & Qt::Key_Return) == Qt::Key_Return);
if (!isKeyExcempt && !hasModifier && !this->shownSingleKeyWarning)
{
this->showEditError(
"Warning: using keybindings without modifiers can lead to not "
"being\nable to use the key for the normal purpose.\nPress the "
"submit button again to do it anyway.");
this->shownSingleKeyWarning = true;
return;
}
// use raw name from item data if possible, otherwise fallback to what the user has entered.
auto actionTemp = this->ui_->actionPicker->currentData();
QString action = this->ui_->actionPicker->currentText();
if (actionTemp.isValid())
{
action = actionTemp.toString();
}
auto hotkey = std::make_shared<Hotkey>(
*category, this->ui_->keyComboEdit->keySequence(), action, arguments,
nameText);
auto keyComboWasEdited =
this->data() &&
this->ui_->keyComboEdit->keySequence() != this->data()->keySequence();
auto nameWasEdited = this->data() && nameText != this->data()->name();
if (isEditing)
{
if (keyComboWasEdited || nameWasEdited)
{
if (getApp()->hotkeys->isDuplicate(hotkey, this->data()->name()))
{
this->showEditError(
"Keybinding needs to be unique in the category.");
return;
}
}
}
else
{
if (getApp()->hotkeys->isDuplicate(hotkey, QString()))
{
this->showEditError(
"Keybinding needs to be unique in the category.");
return;
}
}
this->data_ = hotkey;
this->accept();
}
void EditHotkeyDialog::updatePossibleActions()
{
const auto &hotkeys = getApp()->hotkeys;
auto category = hotkeys->hotkeyCategoryFromName(
this->ui_->categoryPicker->currentData().toString());
if (!category)
{
this->showEditError("Invalid Hotkey Category.");
return;
}
auto currentText = this->ui_->actionPicker->currentData().toString();
if (this->data_ &&
(currentText.isEmpty() || this->data_->category() == category))
{
// is editing
currentText = this->data_->action();
}
this->ui_->actionPicker->clear();
qCDebug(chatterinoHotkeys)
<< "update possible actions for" << (int)*category << currentText;
auto actions = actionNames.find(*category);
if (actions != actionNames.end())
{
int indexToSet = -1;
for (const auto &action : actions->second)
{
this->ui_->actionPicker->addItem(action.second.displayName,
action.first);
if (action.first == currentText)
{
// update action raw name to display name
indexToSet = this->ui_->actionPicker->model()->rowCount() - 1;
}
}
if (indexToSet != -1)
{
this->ui_->actionPicker->setCurrentIndex(indexToSet);
}
}
else
{
qCDebug(chatterinoHotkeys) << "key missing!!!!";
}
}
void EditHotkeyDialog::updateArgumentsInput()
{
auto currentText = this->ui_->actionPicker->currentData().toString();
if (currentText.isEmpty())
{
this->ui_->argumentsEdit->setEnabled(true);
return;
}
const auto &hotkeys = getApp()->hotkeys;
auto category = hotkeys->hotkeyCategoryFromName(
this->ui_->categoryPicker->currentData().toString());
if (!category)
{
this->showEditError("Invalid Hotkey category.");
return;
}
auto allActions = actionNames.find(*category);
if (allActions != actionNames.end())
{
const auto &actionsMap = allActions->second;
auto definition = actionsMap.find(currentText);
if (definition == actionsMap.end())
{
auto text = QString("Newline separated arguments for the action\n"
" - Unable to find action named \"%1\"")
.arg(currentText);
this->ui_->argumentsEdit->setPlaceholderText(text);
return;
}
const ActionDefinition &def = definition->second;
if (def.maxCountArguments != 0)
{
QString text =
"Arguments wrapped in <> are required.\nArguments wrapped in "
"[] "
"are optional.\nArguments are separated by a newline.";
if (!def.argumentDescription.isEmpty())
{
this->ui_->argumentsDescription->setVisible(true);
this->ui_->argumentsDescription->setText(
def.argumentDescription);
}
else
{
this->ui_->argumentsDescription->setVisible(false);
}
text = QString("Arguments wrapped in <> are required.");
if (def.maxCountArguments != def.minCountArguments)
{
text += QString("\nArguments wrapped in [] are optional.");
}
text += "\nArguments are separated by a newline.";
this->ui_->argumentsEdit->setEnabled(true);
this->ui_->argumentsEdit->setPlaceholderText(text);
this->ui_->argumentsLabel->setVisible(true);
this->ui_->argumentsDescription->setVisible(true);
this->ui_->argumentsEdit->setVisible(true);
}
else
{
this->ui_->argumentsLabel->setVisible(false);
this->ui_->argumentsDescription->setVisible(false);
this->ui_->argumentsEdit->setVisible(false);
}
}
}
void EditHotkeyDialog::showEditError(QString errorText)
{
this->ui_->warningLabel->setText(errorText);
this->ui_->warningLabel->show();
}
} // namespace chatterino
+59
View File
@@ -0,0 +1,59 @@
#pragma once
#include "controllers/hotkeys/Hotkey.hpp"
#include <QDialog>
#include <memory>
namespace Ui {
class EditHotkeyDialog;
} // namespace Ui
namespace chatterino {
class EditHotkeyDialog : public QDialog
{
Q_OBJECT
public:
explicit EditHotkeyDialog(const std::shared_ptr<Hotkey> data,
bool isAdd = false, QWidget *parent = nullptr);
~EditHotkeyDialog() final;
std::shared_ptr<Hotkey> data();
protected slots:
/**
* @brief validates the hotkey
*
* fired by the ok button
**/
void afterEdit();
/**
* @brief updates the list of actions based on the category
*
* fired by the category picker changing
**/
void updatePossibleActions();
/**
* @brief updates the arguments description and input visibility
*
* fired by the action picker changing
**/
void updateArgumentsInput();
private:
void showEditError(QString errorText);
Ui::EditHotkeyDialog *ui_;
std::shared_ptr<Hotkey> data_;
bool shownSingleKeyWarning = false;
};
} // namespace chatterino
+235
View File
@@ -0,0 +1,235 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EditHotkeyDialog</class>
<widget class="QDialog" name="EditHotkeyDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Edit Hotkey</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="warningLabel">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
<kerning>true</kerning>
</font>
</property>
<property name="text">
<string>Something went wrong, you should never
see this message :)</string>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="nameLabel">
<property name="text">
<string>Name:</string>
</property>
<property name="buddy">
<cstring>nameEdit</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="nameEdit">
<property name="text">
<string/>
</property>
<property name="frame">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>false</bool>
</property>
<property name="placeholderText">
<string>A description of what the hotkey does.</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="categoryLabel">
<property name="text">
<string>Category:</string>
</property>
<property name="buddy">
<cstring>categoryPicker</cstring>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="actionLabel">
<property name="text">
<string>Action:</string>
</property>
<property name="buddy">
<cstring>actionPicker</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="actionPicker">
<property name="editable">
<bool>false</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="keyComboLabel">
<property name="text">
<string>Keybinding:</string>
</property>
<property name="buddy">
<cstring>keyComboEdit</cstring>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QKeySequenceEdit" name="keyComboEdit"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="argumentsLabel">
<property name="text">
<string>Arguments:</string>
</property>
<property name="buddy">
<cstring>argumentsEdit</cstring>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="argumentsDescription">
<property name="text">
<string>You should never see this message :)</string>
</property>
<property name="buddy">
<cstring>argumentsDescription</cstring>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QPlainTextEdit" name="argumentsEdit">
<property name="plainText">
<string/>
</property>
<property name="placeholderText">
<string>Newline separated arguments for the action</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="categoryPicker"/>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttons">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>nameEdit</tabstop>
<tabstop>categoryPicker</tabstop>
<tabstop>actionPicker</tabstop>
<tabstop>keyComboEdit</tabstop>
<tabstop>argumentsEdit</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttons</sender>
<signal>accepted()</signal>
<receiver>EditHotkeyDialog</receiver>
<slot>afterEdit()</slot>
<hints>
<hint type="sourcelabel">
<x>257</x>
<y>290</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttons</sender>
<signal>rejected()</signal>
<receiver>EditHotkeyDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>325</x>
<y>290</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>categoryPicker</sender>
<signal>currentIndexChanged(int)</signal>
<receiver>EditHotkeyDialog</receiver>
<slot>updatePossibleActions()</slot>
<hints>
<hint type="sourcelabel">
<x>246</x>
<y>85</y>
</hint>
<hint type="destinationlabel">
<x>75</x>
<y>218</y>
</hint>
</hints>
</connection>
<connection>
<sender>actionPicker</sender>
<signal>currentIndexChanged(int)</signal>
<receiver>EditHotkeyDialog</receiver>
<slot>updateArgumentsInput()</slot>
<hints>
<hint type="sourcelabel">
<x>148</x>
<y>119</y>
</hint>
<hint type="destinationlabel">
<x>74</x>
<y>201</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>afterEdit()</slot>
<slot>updatePossibleActions()</slot>
<slot>updateArgumentsInput()</slot>
</slots>
</ui>
+95 -39
View File
@@ -2,7 +2,9 @@
#include "Application.hpp"
#include "common/CompletionModel.hpp"
#include "common/QLogging.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "controllers/hotkeys/HotkeyController.hpp"
#include "debug/Benchmark.hpp"
#include "messages/Message.hpp"
#include "messages/MessageBuilder.hpp"
@@ -10,13 +12,11 @@
#include "providers/twitch/TwitchIrcServer.hpp"
#include "singletons/Emotes.hpp"
#include "singletons/WindowManager.hpp"
#include "util/Shortcut.hpp"
#include "widgets/Notebook.hpp"
#include "widgets/Scrollbar.hpp"
#include "widgets/helper/ChannelView.hpp"
#include <QHBoxLayout>
#include <QShortcut>
#include <QTabWidget>
namespace chatterino {
@@ -137,8 +137,8 @@ EmotePopup::EmotePopup(QWidget *parent)
auto layout = new QVBoxLayout(this);
this->getLayoutContainer()->setLayout(layout);
auto notebook = new Notebook(this);
layout->addWidget(notebook);
this->notebook_ = new Notebook(this);
layout->addWidget(this->notebook_);
layout->setMargin(0);
auto clicked = [this](const Link &link) {
@@ -152,7 +152,7 @@ EmotePopup::EmotePopup(QWidget *parent)
MessageElementFlag::Default, MessageElementFlag::AlwaysShow,
MessageElementFlag::EmoteImages});
view->setEnableScrollingToBottom(false);
notebook->addPage(view, tabTitle);
this->notebook_->addPage(view, tabTitle);
view->linkClicked.connect(clicked);
return view;
@@ -164,43 +164,99 @@ EmotePopup::EmotePopup(QWidget *parent)
this->viewEmojis_ = makeView("Emojis");
this->loadEmojis();
this->addShortcuts();
this->signalHolder_.managedConnect(getApp()->hotkeys->onItemsUpdated,
[this]() {
this->clearShortcuts();
this->addShortcuts();
});
}
void EmotePopup::addShortcuts()
{
HotkeyController::HotkeyMap actions{
{"openTab", // CTRL + 1-8 to open corresponding tab.
[this](std::vector<QString> arguments) -> QString {
if (arguments.size() == 0)
{
qCWarning(chatterinoHotkeys)
<< "openTab shortcut called without arguments. Takes "
"only one argument: tab specifier";
return "openTab shortcut called without arguments. "
"Takes only one argument: tab specifier";
}
auto target = arguments.at(0);
if (target == "last")
{
this->notebook_->selectLastTab();
}
else if (target == "next")
{
this->notebook_->selectNextTab();
}
else if (target == "previous")
{
this->notebook_->selectPreviousTab();
}
else
{
bool ok;
int result = target.toInt(&ok);
if (ok)
{
this->notebook_->selectIndex(result);
}
else
{
qCWarning(chatterinoHotkeys)
<< "Invalid argument for openTab shortcut";
return QString("Invalid argument for openTab "
"shortcut: \"%1\". Use \"last\", "
"\"next\", \"previous\" or an integer.")
.arg(target);
}
}
return "";
}},
{"delete",
[this](std::vector<QString>) -> QString {
this->close();
return "";
}},
{"scrollPage",
[this](std::vector<QString> arguments) -> QString {
if (arguments.size() == 0)
{
qCWarning(chatterinoHotkeys)
<< "scrollPage hotkey called without arguments!";
return "scrollPage hotkey called without arguments!";
}
auto direction = arguments.at(0);
auto channelView = dynamic_cast<ChannelView *>(
this->notebook_->getSelectedPage());
// CTRL + 1-8 to open corresponding tab
for (auto i = 0; i < 8; i++)
{
const auto openTab = [this, i, notebook] {
notebook->selectIndex(i);
};
createWindowShortcut(this, QString("CTRL+%1").arg(i + 1).toUtf8(),
openTab);
}
auto &scrollbar = channelView->getScrollBar();
if (direction == "up")
{
scrollbar.offset(-scrollbar.getLargeChange());
}
else if (direction == "down")
{
scrollbar.offset(scrollbar.getLargeChange());
}
else
{
qCWarning(chatterinoHotkeys) << "Unknown scroll direction";
}
return "";
}},
// Open last tab (first one from right)
createWindowShortcut(this, "CTRL+9", [=] {
notebook->selectLastTab();
});
{"reject", nullptr},
{"accept", nullptr},
{"search", nullptr},
};
// Cycle through tabs
createWindowShortcut(this, "CTRL+Tab", [=] {
notebook->selectNextTab();
});
createWindowShortcut(this, "CTRL+Shift+Tab", [=] {
notebook->selectPreviousTab();
});
// Scroll with Page Up / Page Down
createWindowShortcut(this, "PgUp", [=] {
auto &scrollbar =
dynamic_cast<ChannelView *>(notebook->getSelectedPage())
->getScrollBar();
scrollbar.offset(-scrollbar.getLargeChange());
});
createWindowShortcut(this, "PgDown", [=] {
auto &scrollbar =
dynamic_cast<ChannelView *>(notebook->getSelectedPage())
->getScrollBar();
scrollbar.offset(scrollbar.getLargeChange());
});
this->shortcuts_ = getApp()->hotkeys->shortcutsForCategory(
HotkeyCategory::PopupWindow, actions, this);
}
void EmotePopup::loadChannel(ChannelPtr _channel)
+4
View File
@@ -1,6 +1,7 @@
#pragma once
#include "widgets/BasePopup.hpp"
#include "widgets/Notebook.hpp"
#include <pajlada/signals/signal.hpp>
@@ -28,6 +29,9 @@ private:
ChannelView *channelEmotesView_{};
ChannelView *subEmotesView_{};
ChannelView *viewEmojis_{};
Notebook *notebook_;
void addShortcuts() override;
};
} // namespace chatterino
+80 -15
View File
@@ -1,10 +1,11 @@
#include "SelectChannelDialog.hpp"
#include "Application.hpp"
#include "common/QLogging.hpp"
#include "controllers/hotkeys/HotkeyController.hpp"
#include "providers/twitch/TwitchIrcServer.hpp"
#include "singletons/Theme.hpp"
#include "util/LayoutCreator.hpp"
#include "util/Shortcut.hpp"
#include "widgets/Notebook.hpp"
#include "widgets/dialogs/IrcConnectionEditor.hpp"
#include "widgets/helper/NotebookTab.hpp"
@@ -237,27 +238,15 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent)
this->ui_.notebook->selectIndex(TAB_TWITCH);
this->ui_.twitch.channel->setFocus();
// Shortcuts
createWindowShortcut(this, "Return", [=] {
this->ok();
});
createWindowShortcut(this, "Esc", [=] {
this->close();
});
// restore ui state
// fourtf: enable when releasing irc
if (getSettings()->enableExperimentalIrc)
{
this->ui_.notebook->selectIndex(getSettings()->lastSelectChannelTab);
createWindowShortcut(this, "Ctrl+Tab", [=] {
this->ui_.notebook->selectNextTab();
});
createWindowShortcut(this, "CTRL+Shift+Tab", [=] {
this->ui_.notebook->selectPreviousTab();
});
}
this->addShortcuts();
this->ui_.irc.servers->getTableView()->selectRow(
getSettings()->lastSelectIrcConn);
}
@@ -516,4 +505,80 @@ void SelectChannelDialog::themeChangedEvent()
}
}
void SelectChannelDialog::addShortcuts()
{
HotkeyController::HotkeyMap actions{
{"accept",
[this](std::vector<QString>) -> QString {
this->ok();
return "";
}},
{"reject",
[this](std::vector<QString>) -> QString {
this->close();
return "";
}},
// these make no sense, so they aren't implemented
{"scrollPage", nullptr},
{"search", nullptr},
{"delete", nullptr},
};
if (getSettings()->enableExperimentalIrc)
{
actions.insert(
{"openTab", [this](std::vector<QString> arguments) -> QString {
if (arguments.size() == 0)
{
qCWarning(chatterinoHotkeys)
<< "openTab shortcut called without arguments. "
"Takes only "
"one argument: tab specifier";
return "openTab shortcut called without arguments. "
"Takes only one argument: tab specifier";
}
auto target = arguments.at(0);
if (target == "last")
{
this->ui_.notebook->selectLastTab();
}
else if (target == "next")
{
this->ui_.notebook->selectNextTab();
}
else if (target == "previous")
{
this->ui_.notebook->selectPreviousTab();
}
else
{
bool ok;
int result = target.toInt(&ok);
if (ok)
{
this->ui_.notebook->selectIndex(result);
}
else
{
qCWarning(chatterinoHotkeys)
<< "Invalid argument for openTab shortcut";
return QString("Invalid argument for openTab "
"shortcut: \"%1\". Use \"last\", "
"\"next\", \"previous\" or an integer.")
.arg(target);
}
}
return "";
}});
}
else
{
actions.emplace("openTab", nullptr);
}
this->shortcuts_ = getApp()->hotkeys->shortcutsForCategory(
HotkeyCategory::PopupWindow, actions, this);
}
} // namespace chatterino
@@ -64,6 +64,8 @@ private:
void ok();
friend class EventFilter;
void addShortcuts() override;
};
} // namespace chatterino
+30 -8
View File
@@ -3,9 +3,9 @@
#include "Application.hpp"
#include "common/Args.hpp"
#include "controllers/commands/CommandController.hpp"
#include "controllers/hotkeys/HotkeyController.hpp"
#include "singletons/Resources.hpp"
#include "util/LayoutCreator.hpp"
#include "util/Shortcut.hpp"
#include "widgets/helper/Button.hpp"
#include "widgets/settingspages/AboutPage.hpp"
#include "widgets/settingspages/AccountsPage.hpp"
@@ -30,6 +30,7 @@ SettingsDialog::SettingsDialog(QWidget *parent)
{BaseWindow::Flags::DisableCustomScaling, BaseWindow::Flags::Dialog},
parent)
{
this->setObjectName("SettingsDialog");
this->setWindowTitle("Chatterino Settings");
this->resize(915, 600);
this->themeChangedEvent();
@@ -40,14 +41,35 @@ SettingsDialog::SettingsDialog(QWidget *parent)
this->overrideBackgroundColor_ = QColor("#111111");
this->scaleChangedEvent(this->scale()); // execute twice to width of item
createWindowShortcut(this, "CTRL+F", [this] {
this->ui_.search->setFocus();
this->ui_.search->selectAll();
});
// Disable the ? button in the titlebar until we decide to use it
this->setWindowFlags(this->windowFlags() &
~Qt::WindowContextHelpButtonHint);
this->addShortcuts();
this->signalHolder_.managedConnect(getApp()->hotkeys->onItemsUpdated,
[this]() {
this->clearShortcuts();
this->addShortcuts();
});
}
void SettingsDialog::addShortcuts()
{
HotkeyController::HotkeyMap actions{
{"search",
[this](std::vector<QString>) -> QString {
this->ui_.search->setFocus();
this->ui_.search->selectAll();
return "";
}},
{"delete", nullptr},
{"accept", nullptr},
{"reject", nullptr},
{"scrollPage", nullptr},
{"openTab", nullptr},
};
this->shortcuts_ = getApp()->hotkeys->shortcutsForCategory(
HotkeyCategory::PopupWindow, actions, this);
}
void SettingsDialog::initUi()
@@ -63,7 +85,7 @@ void SettingsDialog::initUi()
.withoutMargin()
.emplace<QLineEdit>()
.assign(&this->ui_.search);
edit->setPlaceholderText("Find in settings... (Ctrl+F)");
edit->setPlaceholderText("Find in settings... (Ctrl+F by default)");
QObject::connect(edit.getElement(), &QLineEdit::textChanged, this,
&SettingsDialog::filterElements);
@@ -172,7 +194,7 @@ void SettingsDialog::addTabs()
this->addTab([]{return new IgnoresPage;}, "Ignores", ":/settings/ignore.svg");
this->addTab([]{return new FiltersPage;}, "Filters", ":/settings/filters.svg");
this->ui_.tabContainer->addSpacing(16);
this->addTab([]{return new KeyboardSettingsPage;}, "Keybindings", ":/settings/keybinds.svg");
this->addTab([]{return new KeyboardSettingsPage;}, "Hotkeys", ":/settings/keybinds.svg");
this->addTab([]{return new ModerationPage;}, "Moderation", ":/settings/moderation.svg", SettingsTabId::Moderation);
this->addTab([]{return new NotificationPage;}, "Live Notifications", ":/settings/notification2.svg");
this->addTab([]{return new ExternalToolsPage;}, "External tools", ":/settings/externaltools.svg");
+1
View File
@@ -60,6 +60,7 @@ private:
void onOkClicked();
void onCancelClicked();
void addShortcuts() override;
struct {
QWidget *tabContainerContainer{};
+44 -5
View File
@@ -3,8 +3,10 @@
#include "Application.hpp"
#include "common/Channel.hpp"
#include "common/NetworkRequest.hpp"
#include "common/QLogging.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "controllers/highlights/HighlightBlacklistUser.hpp"
#include "controllers/hotkeys/HotkeyController.hpp"
#include "messages/Message.hpp"
#include "messages/MessageBuilder.hpp"
#include "providers/IvrApi.hpp"
@@ -18,9 +20,9 @@
#include "util/Helpers.hpp"
#include "util/LayoutCreator.hpp"
#include "util/PostToThread.hpp"
#include "util/Shortcut.hpp"
#include "util/StreamerMode.hpp"
#include "widgets/Label.hpp"
#include "widgets/Scrollbar.hpp"
#include "widgets/helper/ChannelView.hpp"
#include "widgets/helper/EffectLabel.hpp"
#include "widgets/helper/Line.hpp"
@@ -140,10 +142,47 @@ UserInfoPopup::UserInfoPopup(bool closeAutomatically, QWidget *parent)
else
this->setAttribute(Qt::WA_DeleteOnClose);
// Close the popup when Escape is pressed
createWindowShortcut(this, "Escape", [this] {
this->deleteLater();
});
HotkeyController::HotkeyMap actions{
{"delete",
[this](std::vector<QString>) -> QString {
this->deleteLater();
return "";
}},
{"scrollPage",
[this](std::vector<QString> arguments) -> QString {
if (arguments.size() == 0)
{
qCWarning(chatterinoHotkeys)
<< "scrollPage hotkey called without arguments!";
return "scrollPage hotkey called without arguments!";
}
auto direction = arguments.at(0);
auto &scrollbar = this->ui_.latestMessages->getScrollBar();
if (direction == "up")
{
scrollbar.offset(-scrollbar.getLargeChange());
}
else if (direction == "down")
{
scrollbar.offset(scrollbar.getLargeChange());
}
else
{
qCWarning(chatterinoHotkeys) << "Unknown scroll direction";
}
return "";
}},
// these actions make no sense in the context of a usercard, so they aren't implemented
{"reject", nullptr},
{"accept", nullptr},
{"openTab", nullptr},
{"search", nullptr},
};
this->shortcuts_ = getApp()->hotkeys->shortcutsForCategory(
HotkeyCategory::PopupWindow, actions, this);
auto layout = LayoutCreator<QWidget>(this->getLayoutContainer())
.setLayoutType<QVBoxLayout>();