OBSHotkeyWidget.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  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,
  93. [&, CurrentIndex] { AddEdit({0, OBS_KEY_NONE}, CurrentIndex() + 1); });
  94. QObject::connect(remove, &QPushButton::clicked, [&, CurrentIndex] { RemoveEdit(CurrentIndex()); });
  95. QHBoxLayout *subLayout = new QHBoxLayout;
  96. subLayout->setContentsMargins(0, 2, 0, 2);
  97. subLayout->addWidget(edit);
  98. subLayout->addWidget(revert);
  99. subLayout->addWidget(clear);
  100. subLayout->addWidget(add);
  101. subLayout->addWidget(remove);
  102. if (removeButtons.size() == 1)
  103. removeButtons.front()->setEnabled(true);
  104. if (idx != -1) {
  105. revertButtons.insert(begin(revertButtons) + idx, revert);
  106. removeButtons.insert(begin(removeButtons) + idx, remove);
  107. edits.insert(begin(edits) + idx, edit);
  108. } else {
  109. revertButtons.emplace_back(revert);
  110. removeButtons.emplace_back(remove);
  111. edits.emplace_back(edit);
  112. }
  113. layout()->insertLayout(idx, subLayout);
  114. QObject::connect(revert, &QPushButton::clicked, edit, &OBSHotkeyEdit::ResetKey);
  115. QObject::connect(clear, &QPushButton::clicked, edit, &OBSHotkeyEdit::ClearKey);
  116. QObject::connect(edit, &OBSHotkeyEdit::KeyChanged, [&](obs_key_combination) { emit KeyChanged(); });
  117. QObject::connect(edit, &OBSHotkeyEdit::SearchKey, [=](obs_key_combination combo) { emit SearchKey(combo); });
  118. }
  119. void OBSHotkeyWidget::RemoveEdit(size_t idx, bool signal)
  120. {
  121. auto &edit = *(begin(edits) + idx);
  122. if (!obs_key_combination_is_empty(edit->original) && signal) {
  123. changed = true;
  124. }
  125. revertButtons.erase(begin(revertButtons) + idx);
  126. removeButtons.erase(begin(removeButtons) + idx);
  127. edits.erase(begin(edits) + idx);
  128. auto item = layout()->takeAt(static_cast<int>(idx));
  129. QLayoutItem *child = nullptr;
  130. while ((child = item->layout()->takeAt(0))) {
  131. delete child->widget();
  132. delete child;
  133. }
  134. delete item;
  135. if (removeButtons.size() == 1)
  136. removeButtons.front()->setEnabled(false);
  137. emit KeyChanged();
  138. }
  139. void OBSHotkeyWidget::BindingsChanged(void *data, calldata_t *param)
  140. {
  141. auto widget = static_cast<OBSHotkeyWidget *>(data);
  142. auto key = static_cast<obs_hotkey_t *>(calldata_ptr(param, "key"));
  143. QMetaObject::invokeMethod(widget, "HandleChangedBindings", Q_ARG(obs_hotkey_id, obs_hotkey_get_id(key)));
  144. }
  145. void OBSHotkeyWidget::HandleChangedBindings(obs_hotkey_id id_)
  146. {
  147. if (ignoreChangedBindings || id != id_)
  148. return;
  149. std::vector<obs_key_combination_t> bindings;
  150. auto LoadBindings = [&](obs_hotkey_binding_t *binding) {
  151. if (obs_hotkey_binding_get_hotkey_id(binding) != id)
  152. return;
  153. auto get_combo = obs_hotkey_binding_get_key_combination;
  154. bindings.push_back(get_combo(binding));
  155. };
  156. using LoadBindings_t = decltype(&LoadBindings);
  157. obs_enum_hotkey_bindings(
  158. [](void *data, size_t, obs_hotkey_binding_t *binding) {
  159. auto LoadBindings = *static_cast<LoadBindings_t>(data);
  160. LoadBindings(binding);
  161. return true;
  162. },
  163. static_cast<void *>(&LoadBindings));
  164. while (edits.size() > 0)
  165. RemoveEdit(edits.size() - 1, false);
  166. SetKeyCombinations(bindings);
  167. }
  168. void OBSHotkeyWidget::enterEvent(QEnterEvent *event)
  169. {
  170. if (!label)
  171. return;
  172. event->accept();
  173. label->highlightPair(true);
  174. }
  175. void OBSHotkeyWidget::leaveEvent(QEvent *event)
  176. {
  177. if (!label)
  178. return;
  179. event->accept();
  180. label->highlightPair(false);
  181. }