OBSHotkeyWidget.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. /******************************************************************************
  2. Copyright (C) 2014-2015 by Ruwen Hahn <[email protected]>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ******************************************************************************/
  14. #include "OBSHotkeyWidget.hpp"
  15. #include "OBSHotkeyLabel.hpp"
  16. #include <OBSApp.hpp>
  17. #include <QEnterEvent>
  18. #include "moc_OBSHotkeyWidget.cpp"
  19. void OBSHotkeyWidget::SetKeyCombinations(const std::vector<obs_key_combination_t> &combos)
  20. {
  21. if (combos.empty())
  22. AddEdit({0, OBS_KEY_NONE});
  23. for (auto combo : combos)
  24. AddEdit(combo);
  25. }
  26. bool OBSHotkeyWidget::Changed() const
  27. {
  28. return changed || std::any_of(begin(edits), end(edits), [](OBSHotkeyEdit *edit) { return edit->changed; });
  29. }
  30. void OBSHotkeyWidget::Apply()
  31. {
  32. for (auto &edit : edits) {
  33. edit->original = edit->key;
  34. edit->changed = false;
  35. }
  36. changed = false;
  37. for (auto &revertButton : revertButtons)
  38. revertButton->setEnabled(false);
  39. }
  40. void OBSHotkeyWidget::GetCombinations(std::vector<obs_key_combination_t> &combinations) const
  41. {
  42. combinations.clear();
  43. for (auto &edit : edits)
  44. if (!obs_key_combination_is_empty(edit->key))
  45. combinations.emplace_back(edit->key);
  46. }
  47. void OBSHotkeyWidget::Save()
  48. {
  49. std::vector<obs_key_combination_t> combinations;
  50. Save(combinations);
  51. }
  52. void OBSHotkeyWidget::Save(std::vector<obs_key_combination_t> &combinations)
  53. {
  54. GetCombinations(combinations);
  55. Apply();
  56. auto AtomicUpdate = [&]() {
  57. ignoreChangedBindings = true;
  58. obs_hotkey_load_bindings(id, combinations.data(), combinations.size());
  59. ignoreChangedBindings = false;
  60. };
  61. using AtomicUpdate_t = decltype(&AtomicUpdate);
  62. obs_hotkey_update_atomic([](void *d) { (*static_cast<AtomicUpdate_t>(d))(); },
  63. static_cast<void *>(&AtomicUpdate));
  64. }
  65. void OBSHotkeyWidget::AddEdit(obs_key_combination combo, int idx)
  66. {
  67. auto edit = new OBSHotkeyEdit(parentWidget(), combo, settings);
  68. edit->setToolTip(toolTip);
  69. auto revert = new QPushButton;
  70. revert->setProperty("class", "icon-revert");
  71. revert->setToolTip(QTStr("Revert"));
  72. revert->setEnabled(false);
  73. auto clear = new QPushButton;
  74. clear->setProperty("class", "icon-clear");
  75. clear->setToolTip(QTStr("Clear"));
  76. clear->setEnabled(!obs_key_combination_is_empty(combo));
  77. QObject::connect(edit, &OBSHotkeyEdit::KeyChanged, [=](obs_key_combination_t new_combo) {
  78. clear->setEnabled(!obs_key_combination_is_empty(new_combo));
  79. revert->setEnabled(edit->original != new_combo);
  80. });
  81. auto add = new QPushButton;
  82. add->setProperty("class", "icon-plus");
  83. add->setToolTip(QTStr("Add"));
  84. auto remove = new QPushButton;
  85. remove->setProperty("class", "icon-trash");
  86. remove->setToolTip(QTStr("Remove"));
  87. remove->setEnabled(removeButtons.size() > 0);
  88. auto CurrentIndex = [&, remove] {
  89. auto res = std::find(begin(removeButtons), end(removeButtons), remove);
  90. return std::distance(begin(removeButtons), res);
  91. };
  92. QObject::connect(add, &QPushButton::clicked, [&, CurrentIndex] {
  93. AddEdit({0, OBS_KEY_NONE}, CurrentIndex() + 1);
  94. });
  95. QObject::connect(remove, &QPushButton::clicked, [&, CurrentIndex] { RemoveEdit(CurrentIndex()); });
  96. QHBoxLayout *subLayout = new QHBoxLayout;
  97. subLayout->setContentsMargins(0, 2, 0, 2);
  98. subLayout->addWidget(edit);
  99. subLayout->addWidget(revert);
  100. subLayout->addWidget(clear);
  101. subLayout->addWidget(add);
  102. subLayout->addWidget(remove);
  103. if (removeButtons.size() == 1)
  104. removeButtons.front()->setEnabled(true);
  105. if (idx != -1) {
  106. revertButtons.insert(begin(revertButtons) + idx, revert);
  107. removeButtons.insert(begin(removeButtons) + idx, remove);
  108. edits.insert(begin(edits) + idx, edit);
  109. } else {
  110. revertButtons.emplace_back(revert);
  111. removeButtons.emplace_back(remove);
  112. edits.emplace_back(edit);
  113. }
  114. layout()->insertLayout(idx, subLayout);
  115. QObject::connect(revert, &QPushButton::clicked, edit, &OBSHotkeyEdit::ResetKey);
  116. QObject::connect(clear, &QPushButton::clicked, edit, &OBSHotkeyEdit::ClearKey);
  117. QObject::connect(edit, &OBSHotkeyEdit::KeyChanged, [&](obs_key_combination) { emit KeyChanged(); });
  118. QObject::connect(edit, &OBSHotkeyEdit::SearchKey, [=](obs_key_combination combo) { emit SearchKey(combo); });
  119. }
  120. void OBSHotkeyWidget::RemoveEdit(size_t idx, bool signal)
  121. {
  122. auto &edit = *(begin(edits) + idx);
  123. if (!obs_key_combination_is_empty(edit->original) && signal) {
  124. changed = true;
  125. }
  126. revertButtons.erase(begin(revertButtons) + idx);
  127. removeButtons.erase(begin(removeButtons) + idx);
  128. edits.erase(begin(edits) + idx);
  129. auto item = layout()->takeAt(static_cast<int>(idx));
  130. QLayoutItem *child = nullptr;
  131. while ((child = item->layout()->takeAt(0))) {
  132. delete child->widget();
  133. delete child;
  134. }
  135. delete item;
  136. if (removeButtons.size() == 1)
  137. removeButtons.front()->setEnabled(false);
  138. emit KeyChanged();
  139. }
  140. void OBSHotkeyWidget::BindingsChanged(void *data, calldata_t *param)
  141. {
  142. auto widget = static_cast<OBSHotkeyWidget *>(data);
  143. auto key = static_cast<obs_hotkey_t *>(calldata_ptr(param, "key"));
  144. QMetaObject::invokeMethod(widget, "HandleChangedBindings", Q_ARG(obs_hotkey_id, obs_hotkey_get_id(key)));
  145. }
  146. void OBSHotkeyWidget::HandleChangedBindings(obs_hotkey_id id_)
  147. {
  148. if (ignoreChangedBindings || id != id_)
  149. return;
  150. std::vector<obs_key_combination_t> bindings;
  151. auto LoadBindings = [&](obs_hotkey_binding_t *binding) {
  152. if (obs_hotkey_binding_get_hotkey_id(binding) != id)
  153. return;
  154. auto get_combo = obs_hotkey_binding_get_key_combination;
  155. bindings.push_back(get_combo(binding));
  156. };
  157. using LoadBindings_t = decltype(&LoadBindings);
  158. obs_enum_hotkey_bindings(
  159. [](void *data, size_t, obs_hotkey_binding_t *binding) {
  160. auto LoadBindings = *static_cast<LoadBindings_t>(data);
  161. LoadBindings(binding);
  162. return true;
  163. },
  164. static_cast<void *>(&LoadBindings));
  165. while (edits.size() > 0)
  166. RemoveEdit(edits.size() - 1, false);
  167. SetKeyCombinations(bindings);
  168. }
  169. void OBSHotkeyWidget::enterEvent(QEnterEvent *event)
  170. {
  171. if (!label)
  172. return;
  173. event->accept();
  174. label->highlightPair(true);
  175. }
  176. void OBSHotkeyWidget::leaveEvent(QEvent *event)
  177. {
  178. if (!label)
  179. return;
  180. event->accept();
  181. label->highlightPair(false);
  182. }