From e00195ad6f77aad5ebc98bfbce8c63b71873d20d Mon Sep 17 00:00:00 2001 From: Matt Gajownik Date: Mon, 3 Jan 2022 17:41:28 +1100 Subject: [PATCH] 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(). --- UI/forms/OBSBasicSettings.ui | 111 +++++++++++++++----- UI/hotkey-edit.cpp | 8 +- UI/hotkey-edit.hpp | 21 +++- UI/window-basic-settings.cpp | 194 +++++++++++++++++------------------ UI/window-basic-settings.hpp | 7 ++ 5 files changed, 207 insertions(+), 134 deletions(-) diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui index 4b562a174..fee695752 100644 --- a/UI/forms/OBSBasicSettings.ui +++ b/UI/forms/OBSBasicSettings.ui @@ -4690,31 +4690,87 @@ - - - true - - - - - 0 - 0 - 98 - 28 - - - - - QFormLayout::AllNonFixedFieldsGrow - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - 0 - - - + + + + + + + + Basic.Settings.Hotkeys.Filter + + + + + + + + + + Basic.Settings.Hotkeys.FilterByHotkey + + + + + + + + + + + 24 + 24 + + + + + 24 + 24 + + + + + 24 + 24 + + + + Clear + + + true + + + trashIcon + + + + 24 + 24 + + + + + + + + + + true + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 0 + + + + + + @@ -5572,6 +5628,11 @@ QPushButton
url-push-button.hpp
+ + OBSHotkeyEdit + QLineEdit +
hotkey-edit.hpp
+
listWidget diff --git a/UI/hotkey-edit.cpp b/UI/hotkey-edit.cpp index c18b7a3d0..5c27cf250 100644 --- a/UI/hotkey-edit.cpp +++ b/UI/hotkey-edit.cpp @@ -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 &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; diff --git a/UI/hotkey-edit.hpp b/UI/hotkey-edit.hpp index 7d61a7203..a42498a86 100644 --- a/UI/hotkey-edit.hpp +++ b/UI/hotkey-edit.hpp @@ -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 &combos = {}) - : QWidget(nullptr), + : QWidget(parent), id(id), name(name), bindingsChanged(obs_get_signal_handler(), diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index c6991fe0d..05b571c87 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -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>; 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 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(label->widget()); - if (!item) - continue; - - item->widget->GetCombinations(combos); - QString fullname = - item->property("fullName").value(); - - 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 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(hotkeys->layout()); + + for (int i = 0; i < hotkeysLayout->rowCount(); i++) { + auto label = hotkeysLayout->itemAt(i, QFormLayout::LabelRole); + if (!label) + continue; + + OBSHotkeyLabel *item = + qobject_cast(label->widget()); + if (!item) + continue; + + item->widget->GetCombinations(combos); + QString fullname = item->property("fullName").value(); + + 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) diff --git a/UI/window-basic-settings.hpp b/UI/window-basic-settings.hpp index b27c9e143..feed0c429 100644 --- a/UI/window-basic-settings.hpp +++ b/UI/window-basic-settings.hpp @@ -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();