From d28e80ae39b1e28f7d6b66544dce8a197957ec1a Mon Sep 17 00:00:00 2001 From: Palana Date: Sat, 1 Nov 2014 21:50:36 +0100 Subject: [PATCH] UI: Add hotkeys page in settings dialog --- obs/data/locale/en-US.ini | 4 + obs/forms/OBSBasicSettings.ui | 27 ++ ...preferences-desktop-keyboard-shortcuts.png | Bin 0 -> 4100 bytes obs/forms/obs.qrc | 1 + obs/window-basic-settings.cpp | 360 ++++++++++++++++++ obs/window-basic-settings.hpp | 15 +- 6 files changed, 406 insertions(+), 1 deletion(-) create mode 100644 obs/forms/images/settings/preferences-desktop-keyboard-shortcuts.png diff --git a/obs/data/locale/en-US.ini b/obs/data/locale/en-US.ini index 3bb763897..33f606f12 100644 --- a/obs/data/locale/en-US.ini +++ b/obs/data/locale/en-US.ini @@ -353,6 +353,10 @@ Basic.AdvAudio.Panning="Panning" Basic.AdvAudio.SyncOffset="Sync Offset (ms)" Basic.AdvAudio.AudioTracks="Tracks" +# basic mode 'hotkeys' settings +Basic.Settings.Hotkeys="Hotkeys" +Basic.Settings.Hotkeys.Pair="Key combinations shared with '%1' act as toggles" + # hotkeys that may lack translation on certain operating systems Hotkeys.Insert="Insert" Hotkeys.Delete="Delete" diff --git a/obs/forms/OBSBasicSettings.ui b/obs/forms/OBSBasicSettings.ui index 9726f64fb..efe763354 100644 --- a/obs/forms/OBSBasicSettings.ui +++ b/obs/forms/OBSBasicSettings.ui @@ -87,6 +87,15 @@ :/settings/images/settings/video-display-3.png:/settings/images/settings/video-display-3.png + + + Basic.Settings.Hotkeys + + + + :/settings/images/settings/preferences-desktop-keyboard-shortcuts.png:/settings/images/settings/preferences-desktop-keyboard-shortcuts.png + + Basic.Settings.Advanced @@ -2330,6 +2339,24 @@ + + + true + + + + + 0 + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + diff --git a/obs/forms/images/settings/preferences-desktop-keyboard-shortcuts.png b/obs/forms/images/settings/preferences-desktop-keyboard-shortcuts.png new file mode 100644 index 0000000000000000000000000000000000000000..95a3aa87e45f6cfa819e5c063fd5a59e396934a0 GIT binary patch literal 4100 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 WpVgou%kkcqUVw@MTD}a04fzj6iGf@I literal 0 HcmV?d00001 diff --git a/obs/forms/obs.qrc b/obs/forms/obs.qrc index e6a14d7ef..60ab59a4b 100644 --- a/obs/forms/obs.qrc +++ b/obs/forms/obs.qrc @@ -22,5 +22,6 @@ images/settings/applications-system-2.png images/settings/system-settings-3.png images/settings/network-bluetooth.png + images/settings/preferences-desktop-keyboard-shortcuts.png diff --git a/obs/window-basic-settings.cpp b/obs/window-basic-settings.cpp index 483bf8735..99b637e78 100644 --- a/obs/window-basic-settings.cpp +++ b/obs/window-basic-settings.cpp @@ -28,7 +28,10 @@ #include #include #include +#include +#include "hotkey-edit.hpp" +#include "source-label.hpp" #include "obs-app.hpp" #include "platform.hpp" #include "properties-view.hpp" @@ -313,6 +316,26 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) LoadEncoderTypes(); LoadColorRanges(); LoadFormats(); + + auto ReloadHotkeys = [](void *data, calldata_t*) + { + auto settings = static_cast(data); + QMetaObject::invokeMethod(settings, "ReloadHotkeys"); + }; + hotkeyRegistered.Connect(obs_get_signal_handler(), "hotkey_register", + ReloadHotkeys, this); + + auto ReloadHotkeysIgnore = [](void *data, calldata_t *param) + { + auto settings = static_cast(data); + auto key = static_cast( + calldata_ptr(param,"key")); + QMetaObject::invokeMethod(settings, "ReloadHotkeys", + Q_ARG(obs_hotkey_id, obs_hotkey_get_id(key))); + }; + hotkeyUnregistered.Connect(obs_get_signal_handler(), + "hotkey_unregister", ReloadHotkeysIgnore, this); + LoadSettings(false); } @@ -1278,6 +1301,290 @@ void OBSBasicSettings::LoadAdvancedSettings() loading = false; } +template +static inline void LayoutHotkey(obs_hotkey_id id, obs_hotkey_t *key, Func &&fun, + const map> &keys) +{ + auto *label = new OBSHotkeyLabel; + label->setText(obs_hotkey_get_description(key)); + + OBSHotkeyWidget *hw = nullptr; + + auto combos = keys.find(id); + if (combos == std::end(keys)) + hw = new OBSHotkeyWidget(id, obs_hotkey_get_name(key)); + else + hw = new OBSHotkeyWidget(id, obs_hotkey_get_name(key), + combos->second); + + hw->label = label; + label->widget = hw; + + fun(key, label, hw); +} + +template +static QLabel *makeLabel(T &t, Func &&getName) +{ + return new QLabel(getName(t)); +} + +template +static QLabel *makeLabel(const OBSSource &source, Func &&) +{ + return new OBSSourceLabel(source); +} + +template +static inline void AddHotkeys(QFormLayout &layout, + Func &&getName, std::vector< + std::tuple, QPointer> + > &hotkeys) +{ + if (hotkeys.empty()) + return; + + auto line = new QFrame(); + line->setFrameShape(QFrame::HLine); + line->setFrameShadow(QFrame::Sunken); + + layout.setItem(layout.rowCount(), QFormLayout::SpanningRole, + new QSpacerItem(0, 10)); + layout.addRow(line); + + using tuple_type = + std::tuple, QPointer>; + + stable_sort(begin(hotkeys), end(hotkeys), + [&](const tuple_type &a, const tuple_type &b) + { + const auto &o_a = get<0>(a); + const auto &o_b = get<0>(b); + return o_a != o_b && + string(getName(o_a)) < + getName(o_b); + }); + + string prevName; + for (const auto &hotkey : hotkeys) { + const auto &o = get<0>(hotkey); + const char *name = getName(o); + if (prevName != name) { + prevName = name; + layout.setItem(layout.rowCount(), + QFormLayout::SpanningRole, + new QSpacerItem(0, 10)); + layout.addRow(makeLabel(o, getName)); + } + + auto hlabel = get<1>(hotkey); + auto widget = get<2>(hotkey); + layout.addRow(hlabel, widget); + } +} + +void OBSBasicSettings::LoadHotkeySettings(obs_hotkey_id ignoreKey) +{ + hotkeys.clear(); + ui->hotkeyPage->takeWidget()->deleteLater(); + + using keys_t = map>; + keys_t keys; + obs_enum_hotkey_bindings([](void *data, + size_t, obs_hotkey_binding_t *binding) + { + auto &keys = *static_cast(data); + + keys[obs_hotkey_binding_get_hotkey_id(binding)].emplace_back( + obs_hotkey_binding_get_key_combination(binding)); + + return true; + }, &keys); + + auto layout = new QFormLayout(); + layout->setVerticalSpacing(0); + layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); + layout->setLabelAlignment( + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + auto widget = new QWidget(); + widget->setLayout(layout); + ui->hotkeyPage->setWidget(widget); + + using namespace std; + using encoders_elem_t = + tuple, QPointer>; + using outputs_elem_t = + tuple, QPointer>; + using services_elem_t = + tuple, QPointer>; + using sources_elem_t = + tuple, QPointer>; + vector encoders; + vector outputs; + vector services; + vector scenes; + vector sources; + + vector pairIds; + map> pairLabels; + + using std::move; + + auto HandleEncoder = [&](void *registerer, OBSHotkeyLabel *label, + OBSHotkeyWidget *hw) + { + auto weak_encoder = + static_cast(registerer); + auto encoder = OBSGetStrongRef(weak_encoder); + + if (!encoder) + return true; + + encoders.emplace_back(move(encoder), label, hw); + return false; + }; + + auto HandleOutput = [&](void *registerer, OBSHotkeyLabel *label, + OBSHotkeyWidget *hw) + { + auto weak_output = static_cast(registerer); + auto output = OBSGetStrongRef(weak_output); + + if (!output) + return true; + + outputs.emplace_back(move(output), label, hw); + return false; + }; + + auto HandleService = [&](void *registerer, OBSHotkeyLabel *label, + OBSHotkeyWidget *hw) + { + auto weak_service = + static_cast(registerer); + auto service = OBSGetStrongRef(weak_service); + + if (!service) + return true; + + services.emplace_back(move(service), label, hw); + return false; + }; + + auto HandleSource = [&](void *registerer, OBSHotkeyLabel *label, + OBSHotkeyWidget *hw) + { + auto weak_source = static_cast(registerer); + auto source = OBSGetStrongRef(weak_source); + + if (!source) + return true; + + if (obs_scene_from_source(source)) + scenes.emplace_back(source, label, hw); + else + sources.emplace_back(source, label, hw); + + return false; + }; + + auto RegisterHotkey = [&](obs_hotkey_t *key, OBSHotkeyLabel *label, + OBSHotkeyWidget *hw) + { + auto registerer_type = obs_hotkey_get_registerer_type(key); + void *registerer = obs_hotkey_get_registerer(key); + + obs_hotkey_id partner = obs_hotkey_get_pair_partner_id(key); + if (partner != OBS_INVALID_HOTKEY_ID) { + pairLabels.emplace(obs_hotkey_get_id(key), + make_pair(partner, label)); + pairIds.push_back(obs_hotkey_get_id(key)); + } + + using std::move; + + switch (registerer_type) { + case OBS_HOTKEY_REGISTERER_FRONTEND: + layout->addRow(label, hw); + break; + + case OBS_HOTKEY_REGISTERER_ENCODER: + if (HandleEncoder(registerer, label, hw)) + return; + break; + + case OBS_HOTKEY_REGISTERER_OUTPUT: + if (HandleOutput(registerer, label, hw)) + return; + break; + + case OBS_HOTKEY_REGISTERER_SERVICE: + if (HandleService(registerer, label, hw)) + return; + break; + + case OBS_HOTKEY_REGISTERER_SOURCE: + if (HandleSource(registerer, label, hw)) + return; + break; + } + + hotkeys.emplace_back(registerer_type == + OBS_HOTKEY_REGISTERER_FRONTEND, hw); + connect(hw, &OBSHotkeyWidget::KeyChanged, + this, &OBSBasicSettings::HotkeysChanged); + }; + + auto data = make_tuple(RegisterHotkey, std::move(keys), ignoreKey); + using data_t = decltype(data); + obs_enum_hotkeys([](void *data, obs_hotkey_id id, obs_hotkey_t *key) + { + data_t &d = *static_cast(data); + if (id != get<2>(d)) + LayoutHotkey(id, key, get<0>(d), get<1>(d)); + return true; + }, &data); + + for (auto keyId : pairIds) { + auto data1 = pairLabels.find(keyId); + if (data1 == end(pairLabels)) + continue; + + auto &label1 = data1->second.second; + if (label1->pairPartner) + continue; + + auto data2 = pairLabels.find(data1->second.first); + if (data2 == end(pairLabels)) + continue; + + auto &label2 = data2->second.second; + if (label2->pairPartner) + continue; + + QString tt = QTStr("Basic.Settings.Hotkeys.Pair"); + auto name1 = label1->text(); + auto name2 = label2->text(); + + auto Update = [&](OBSHotkeyLabel *label, const QString &name, + OBSHotkeyLabel *other, const QString &otherName) + { + label->setToolTip(tt.arg(otherName)); + label->setText(name + " ✳"); + label->pairPartner = other; + }; + Update(label1, name1, label2, name2); + Update(label2, name2, label1, name1); + } + + AddHotkeys(*layout, obs_output_get_name, outputs); + AddHotkeys(*layout, obs_source_get_name, scenes); + AddHotkeys(*layout, obs_source_get_name, sources); + AddHotkeys(*layout, obs_encoder_get_name, encoders); + AddHotkeys(*layout, obs_service_get_name, services); +} + void OBSBasicSettings::LoadSettings(bool changedOnly) { if (!changedOnly || generalChanged) @@ -1290,6 +1597,8 @@ void OBSBasicSettings::LoadSettings(bool changedOnly) LoadAudioSettings(); if (!changedOnly || videoChanged) LoadVideoSettings(); + if (!changedOnly || hotkeysChanged) + LoadHotkeySettings(); if (!changedOnly || advancedChanged) LoadAdvancedSettings(); } @@ -1560,6 +1869,33 @@ void OBSBasicSettings::SaveAudioSettings() main->ResetAudioDevices(); } +void OBSBasicSettings::SaveHotkeySettings() +{ + const auto &config = main->Config(); + + using namespace std; + + std::vector combinations; + for (auto &hotkey : hotkeys) { + auto &hw = *hotkey.second; + if (!hw.Changed()) + continue; + + hw.Save(combinations); + + if (!hotkey.first) + continue; + + obs_data_array_t *array = obs_hotkey_save(hw.id); + obs_data_t *data = obs_data_create(); + obs_data_set_array(data, "bindings", array); + const char *json = obs_data_get_json(data); + config_set_string(config, "Hotkeys", hw.name.c_str(), json); + obs_data_release(data); + obs_data_array_release(array); + } +} + void OBSBasicSettings::SaveSettings() { if (generalChanged) @@ -1572,6 +1908,8 @@ void OBSBasicSettings::SaveSettings() SaveAudioSettings(); if (videoChanged) SaveVideoSettings(); + if (hotkeysChanged) + SaveHotkeySettings(); if (advancedChanged) SaveAdvancedSettings(); @@ -1937,6 +2275,28 @@ void OBSBasicSettings::VideoChanged() } } +void OBSBasicSettings::HotkeysChanged() +{ + using namespace std; + if (loading) + return; + + hotkeysChanged = any_of(begin(hotkeys), end(hotkeys), + [](const pair> &hotkey) + { + const auto &hw = *hotkey.second; + return hw.Changed(); + }); + + if (hotkeysChanged) + EnableApplyButton(true); +} + +void OBSBasicSettings::ReloadHotkeys(obs_hotkey_id ignoreKey) +{ + LoadHotkeySettings(ignoreKey); +} + void OBSBasicSettings::AdvancedChanged() { if (!loading) { diff --git a/obs/window-basic-settings.hpp b/obs/window-basic-settings.hpp index 93bf7b9d2..85fdfb68d 100644 --- a/obs/window-basic-settings.hpp +++ b/obs/window-basic-settings.hpp @@ -31,6 +31,7 @@ class OBSBasic; class QAbstractButton; class QComboBox; class OBSPropertiesView; +class OBSHotkeyWidget; #include "ui_OBSBasicSettings.h" @@ -58,11 +59,13 @@ private: OBSBasic *main; std::unique_ptr ui; + bool generalChanged = false; bool stream1Changed = false; bool outputsChanged = false; bool audioChanged = false; bool videoChanged = false; + bool hotkeysChanged = false; bool advancedChanged = false; int pageIndex = 0; bool loading = true; @@ -74,6 +77,10 @@ private: OBSPropertiesView *streamEncoderProps = nullptr; OBSPropertiesView *recordEncoderProps = nullptr; + std::vector>> hotkeys; + OBSSignal hotkeyRegistered; + OBSSignal hotkeyUnregistered; + void SaveCombo(QComboBox *widget, const char *section, const char *value); void SaveComboData(QComboBox *widget, const char *section, @@ -91,7 +98,8 @@ private: inline bool Changed() const { return generalChanged || outputsChanged || stream1Changed || - audioChanged || videoChanged || advancedChanged; + audioChanged || videoChanged || advancedChanged || + hotkeysChanged; } inline void EnableApplyButton(bool en) @@ -106,6 +114,7 @@ private: outputsChanged = false; audioChanged = false; videoChanged = false; + hotkeysChanged = false; advancedChanged= false; EnableApplyButton(false); } @@ -125,6 +134,7 @@ private: void LoadOutputSettings(); void LoadAudioSettings(); void LoadVideoSettings(); + void LoadHotkeySettings(obs_hotkey_id ignoreKey=OBS_INVALID_HOTKEY_ID); void LoadAdvancedSettings(); void LoadSettings(bool changedOnly); @@ -165,6 +175,7 @@ private: void SaveOutputSettings(); void SaveAudioSettings(); void SaveVideoSettings(); + void SaveHotkeySettings(); void SaveAdvancedSettings(); void SaveSettings(); @@ -199,6 +210,8 @@ private slots: void VideoChanged(); void VideoChangedResolution(); void VideoChangedRestart(); + void HotkeysChanged(); + void ReloadHotkeys(obs_hotkey_id ignoreKey=OBS_INVALID_HOTKEY_ID); void AdvancedChanged(); void AdvancedChangedRestart();