UI: Don't recreate entire Hotkey Settings tab

This replaces the programmatic generation of the hotkeys tab with XML.
Also fixes a memory leak. Reuses concepts from LoadAudioSources().
master
Matt Gajownik 2022-01-03 17:41:28 +11:00 committed by Jim
parent a46f56a3e9
commit e00195ad6f
5 changed files with 207 additions and 134 deletions

View File

@ -4690,31 +4690,87 @@
</item>
</layout>
</widget>
<widget class="QScrollArea" name="hotkeyPage">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="hotkeyWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>98</width>
<height>28</height>
</rect>
</property>
<layout class="QFormLayout" name="hotkeyLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="verticalSpacing">
<number>0</number>
</property>
</layout>
</widget>
<widget class="QWidget" name="hotkeyPage">
<layout class="QVBoxLayout" name="verticalLayout_25">
<item>
<layout class="QGridLayout" name="hotkeySearchLayout">
<item row="0" column="0">
<widget class="QLabel" name="hotkeySearchLabel">
<property name="text">
<string>Basic.Settings.Hotkeys.Filter</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="hotkeyFilterSearch"/>
</item>
<item row="0" column="2">
<widget class="QLabel" name="hotkeyFilterLabel">
<property name="text">
<string>Basic.Settings.Hotkeys.FilterByHotkey</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="OBSHotkeyEdit" name="hotkeyFilterInput"/>
</item>
<item row="0" column="4">
<widget class="QPushButton" name="hotkeyFilterReset">
<property name="minimumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="baseSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolTip">
<string>Clear</string>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string>trashIcon</string>
</property>
<property name="fixedSize" stdset="0">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QScrollArea" name="hotkeyScrollArea">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="hotkeyScrollContents">
<layout class="QFormLayout" name="hotkeyFormLayout">
<property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="verticalSpacing">
<number>0</number>
</property>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="advancedPage">
<layout class="QVBoxLayout" name="verticalLayout_16">
@ -5572,6 +5628,11 @@
<extends>QPushButton</extends>
<header>url-push-button.hpp</header>
</customwidget>
<customwidget>
<class>OBSHotkeyEdit</class>
<extends>QLineEdit</extends>
<header>hotkey-edit.hpp</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>listWidget</tabstop>

View File

@ -175,7 +175,7 @@ void OBSHotkeyEdit::ClearKey()
void OBSHotkeyEdit::UpdateDuplicationState()
{
if (dupeIcon->isVisible() != hasDuplicate) {
if (dupeIcon && dupeIcon->isVisible() != hasDuplicate) {
dupeIcon->setVisible(hasDuplicate);
update();
}
@ -194,8 +194,8 @@ void OBSHotkeyEdit::InitSignalHandler()
void OBSHotkeyEdit::CreateDupeIcon()
{
dupeIcon = this->addAction(settings->GetHotkeyConflictIcon(),
ActionPosition::TrailingPosition);
dupeIcon = addAction(settings->GetHotkeyConflictIcon(),
ActionPosition::TrailingPosition);
dupeIcon->setToolTip(QTStr("Basic.Settings.Hotkeys.DuplicateWarning"));
QObject::connect(dupeIcon, &QAction::triggered,
[=] { emit SearchKey(key); });
@ -274,7 +274,7 @@ void OBSHotkeyWidget::Save(std::vector<obs_key_combination_t> &combinations)
void OBSHotkeyWidget::AddEdit(obs_key_combination combo, int idx)
{
auto edit = new OBSHotkeyEdit(combo, settings);
auto edit = new OBSHotkeyEdit(parentWidget(), combo, settings);
edit->setToolTip(toolTip);
auto revert = new QPushButton;

View File

@ -62,9 +62,9 @@ class OBSHotkeyEdit : public QLineEdit {
Q_OBJECT;
public:
OBSHotkeyEdit(obs_key_combination_t original,
OBSHotkeyEdit(QWidget *parent, obs_key_combination_t original,
OBSBasicSettings *settings)
: QLineEdit(nullptr), original(original), settings(settings)
: QLineEdit(parent), original(original), settings(settings)
{
#ifdef __APPLE__
// disable the input cursor on OSX, focus should be clear
@ -77,6 +77,19 @@ public:
CreateDupeIcon();
ResetKey();
}
OBSHotkeyEdit(QWidget *parent = nullptr)
: QLineEdit(parent), original({}), settings(nullptr)
{
#ifdef __APPLE__
// disable the input cursor on OSX, focus should be clear
// enough with the default focus frame
setReadOnly(true);
#endif
setAttribute(Qt::WA_InputMethodEnabled, false);
setAttribute(Qt::WA_MacShowFocusRect, true);
InitSignalHandler();
ResetKey();
}
obs_key_combination_t original;
obs_key_combination_t key;
@ -116,10 +129,10 @@ class OBSHotkeyWidget : public QWidget {
Q_OBJECT;
public:
OBSHotkeyWidget(obs_hotkey_id id, std::string name,
OBSHotkeyWidget(QWidget *parent, obs_hotkey_id id, std::string name,
OBSBasicSettings *settings,
const std::vector<obs_key_combination_t> &combos = {})
: QWidget(nullptr),
: QWidget(parent),
id(id),
name(name),
bindingsChanged(obs_get_signal_handler(),

View File

@ -2620,11 +2620,11 @@ LayoutHotkey(OBSBasicSettings *settings, obs_hotkey_id id, obs_hotkey_t *key,
auto combos = keys.find(id);
if (combos == std::end(keys))
hw = new OBSHotkeyWidget(id, obs_hotkey_get_name(key),
hw = new OBSHotkeyWidget(settings, id, obs_hotkey_get_name(key),
settings);
else
hw = new OBSHotkeyWidget(id, obs_hotkey_get_name(key), settings,
combos->second);
hw = new OBSHotkeyWidget(settings, id, obs_hotkey_get_name(key),
settings, combos->second);
hw->label = label;
label->widget = hw;
@ -2700,7 +2700,17 @@ static inline void AddHotkeys(
void OBSBasicSettings::LoadHotkeySettings(obs_hotkey_id ignoreKey)
{
hotkeys.clear();
ui->hotkeyPage->takeWidget()->deleteLater();
if (ui->hotkeyFormLayout->rowCount() > 0) {
QLayoutItem *forDeletion = ui->hotkeyFormLayout->takeAt(0);
forDeletion->widget()->deleteLater();
delete forDeletion;
}
ui->hotkeyFilterSearch->blockSignals(true);
ui->hotkeyFilterInput->blockSignals(true);
ui->hotkeyFilterSearch->setText("");
ui->hotkeyFilterInput->ResetKey();
ui->hotkeyFilterSearch->blockSignals(false);
ui->hotkeyFilterInput->blockSignals(false);
using keys_t = map<obs_hotkey_id, vector<obs_key_combination_t>>;
keys_t keys;
@ -2717,104 +2727,14 @@ void OBSBasicSettings::LoadHotkeySettings(obs_hotkey_id ignoreKey)
},
&keys);
auto layout = new QVBoxLayout();
ui->hotkeyPage->setLayout(layout);
auto widget = new QWidget();
auto scrollArea = new QScrollArea();
scrollArea->setWidgetResizable(true);
scrollArea->setWidget(widget);
auto hotkeysLayout = new QFormLayout();
QFormLayout *hotkeysLayout = new QFormLayout();
hotkeysLayout->setVerticalSpacing(0);
hotkeysLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
hotkeysLayout->setLabelAlignment(Qt::AlignRight | Qt::AlignTrailing |
Qt::AlignVCenter);
widget->setLayout(hotkeysLayout);
auto filterLayout = new QGridLayout();
auto filterLabel = new QLabel(QTStr("Basic.Settings.Hotkeys.Filter"));
auto filter = new QLineEdit();
auto filterHotkeyLabel =
new QLabel(QTStr("Basic.Settings.Hotkeys.FilterByHotkey"));
auto filterHotkeyInput = new OBSHotkeyEdit({}, this);
auto filterReset = new QPushButton;
filterReset->setProperty("themeID", "trashIcon");
filterReset->setToolTip(QTStr("Clear"));
filterReset->setFixedSize(24, 24);
filterReset->setFlat(true);
auto setRowVisible = [=](int row, bool visible, QLayoutItem *label) {
label->widget()->setVisible(visible);
auto field = hotkeysLayout->itemAt(row, QFormLayout::FieldRole);
if (field)
field->widget()->setVisible(visible);
};
auto searchFunction = [=](const QString &text,
obs_key_combination_t filterCombo) {
std::vector<obs_key_combination_t> combos;
bool showHotkey;
scrollArea->ensureVisible(0, 0);
scrollArea->setUpdatesEnabled(false);
for (int i = 0; i < hotkeysLayout->rowCount(); i++) {
auto label = hotkeysLayout->itemAt(
i, QFormLayout::LabelRole);
if (!label)
continue;
OBSHotkeyLabel *item =
qobject_cast<OBSHotkeyLabel *>(label->widget());
if (!item)
continue;
item->widget->GetCombinations(combos);
QString fullname =
item->property("fullName").value<QString>();
showHotkey =
text.isEmpty() ||
fullname.toLower().contains(text.toLower());
if (showHotkey &&
!obs_key_combination_is_empty(filterCombo)) {
showHotkey = false;
for (auto combo : combos) {
if (combo == filterCombo) {
showHotkey = true;
continue;
}
}
}
setRowVisible(i, showHotkey, label);
}
scrollArea->setUpdatesEnabled(true);
};
connect(filter, &QLineEdit::textChanged, this, [=](const QString text) {
searchFunction(text, filterHotkeyInput->key);
});
connect(filterHotkeyInput, &OBSHotkeyEdit::KeyChanged, this,
[=](obs_key_combination_t combo) {
searchFunction(filter->text(), combo);
});
connect(filterReset, &QPushButton::clicked, this, [=]() {
filter->setText("");
filterHotkeyInput->ResetKey();
});
filterLayout->addWidget(filterLabel, 0, 0);
filterLayout->addWidget(filter, 0, 1);
filterLayout->addWidget(filterHotkeyLabel, 0, 2);
filterLayout->addWidget(filterHotkeyInput, 0, 3);
filterLayout->addWidget(filterReset, 0, 4);
layout->addLayout(filterLayout);
layout->addWidget(scrollArea);
auto hotkeyChildWidget = new QWidget();
hotkeyChildWidget->setLayout(hotkeysLayout);
ui->hotkeyFormLayout->addRow(hotkeyChildWidget);
using namespace std;
using encoders_elem_t =
@ -2938,9 +2858,9 @@ void OBSBasicSettings::LoadHotkeySettings(obs_hotkey_id ignoreKey)
});
connect(hw, &OBSHotkeyWidget::SearchKey,
[=](obs_key_combination_t combo) {
filter->setText("");
filterHotkeyInput->HandleNewKey(combo);
filterHotkeyInput->KeyChanged(combo);
ui->hotkeyFilterSearch->setText("");
ui->hotkeyFilterInput->HandleNewKey(combo);
ui->hotkeyFilterInput->KeyChanged(combo);
});
};
@ -4343,6 +4263,78 @@ void OBSBasicSettings::HotkeysChanged()
EnableApplyButton(true);
}
void OBSBasicSettings::SearchHotkeys(const QString &text,
obs_key_combination_t filterCombo)
{
if (ui->hotkeyFormLayout->rowCount() == 0)
return;
std::vector<obs_key_combination_t> combos;
bool showHotkey;
ui->hotkeyScrollArea->ensureVisible(0, 0);
ui->hotkeyScrollArea->setUpdatesEnabled(false);
QLayoutItem *hotkeysItem = ui->hotkeyFormLayout->itemAt(0);
QWidget *hotkeys = hotkeysItem->widget();
if (!hotkeys)
return;
QFormLayout *hotkeysLayout =
qobject_cast<QFormLayout *>(hotkeys->layout());
for (int i = 0; i < hotkeysLayout->rowCount(); i++) {
auto label = hotkeysLayout->itemAt(i, QFormLayout::LabelRole);
if (!label)
continue;
OBSHotkeyLabel *item =
qobject_cast<OBSHotkeyLabel *>(label->widget());
if (!item)
continue;
item->widget->GetCombinations(combos);
QString fullname = item->property("fullName").value<QString>();
showHotkey = text.isEmpty() ||
fullname.toLower().contains(text.toLower());
if (showHotkey && !obs_key_combination_is_empty(filterCombo)) {
showHotkey = false;
for (auto combo : combos) {
if (combo == filterCombo) {
showHotkey = true;
continue;
}
}
}
label->widget()->setVisible(showHotkey);
auto field = hotkeysLayout->itemAt(i, QFormLayout::FieldRole);
if (field)
field->widget()->setVisible(showHotkey);
}
ui->hotkeyScrollArea->setUpdatesEnabled(true);
}
void OBSBasicSettings::on_hotkeyFilterReset_clicked()
{
ui->hotkeyFilterSearch->setText("");
ui->hotkeyFilterInput->ResetKey();
}
void OBSBasicSettings::on_hotkeyFilterSearch_textChanged(const QString text)
{
SearchHotkeys(text, ui->hotkeyFilterInput->key);
}
void OBSBasicSettings::on_hotkeyFilterInput_KeyChanged(
obs_key_combination_t combo)
{
SearchHotkeys(ui->hotkeyFilterSearch->text(), combo);
}
static bool MarkHotkeyConflicts(OBSHotkeyLabel *item1, OBSHotkeyLabel *item2)
{
if (item1->pairPartner == item2)

View File

@ -268,6 +268,10 @@ private slots:
void on_useStreamKey_clicked();
void on_useAuth_toggled();
void on_hotkeyFilterReset_clicked();
void on_hotkeyFilterSearch_textChanged(const QString text);
void on_hotkeyFilterInput_KeyChanged(obs_key_combination_t combo);
private:
/* output */
void LoadSimpleOutputSettings();
@ -303,6 +307,9 @@ private:
void SaveAdvancedSettings();
void SaveSettings();
void SearchHotkeys(const QString &text,
obs_key_combination_t filterCombo);
void UpdateSimpleOutStreamDelayEstimate();
void UpdateAdvOutStreamDelayEstimate();