OBSActionRow.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. /******************************************************************************
  2. Copyright (C) 2023 by Dennis Sädtler <[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 <QComboBox>
  15. #include <QApplication>
  16. #include <QSizePolicy>
  17. #include "OBSActionRow.hpp"
  18. #include <QSvgRenderer>
  19. OBSActionRowWidget::OBSActionRowWidget(QWidget *parent) : OBSActionRow(parent)
  20. {
  21. layout = new QGridLayout(this);
  22. layout->setVerticalSpacing(0);
  23. layout->setContentsMargins(0, 0, 0, 0);
  24. labelLayout = new QVBoxLayout();
  25. labelLayout->setSpacing(0);
  26. labelLayout->setContentsMargins(0, 0, 0, 0);
  27. setFocusPolicy(Qt::StrongFocus);
  28. setLayout(layout);
  29. layout->setColumnMinimumWidth(0, 0);
  30. layout->setColumnStretch(0, 0);
  31. layout->setColumnStretch(1, 40);
  32. layout->setColumnStretch(2, 55);
  33. nameLabel = new QLabel();
  34. nameLabel->setVisible(false);
  35. OBSIdianUtils::addClass(nameLabel, "title");
  36. descriptionLabel = new QLabel();
  37. descriptionLabel->setVisible(false);
  38. OBSIdianUtils::addClass(descriptionLabel, "description");
  39. labelLayout->addWidget(nameLabel);
  40. labelLayout->addWidget(descriptionLabel);
  41. layout->addLayout(labelLayout, 0, 1, Qt::AlignLeft);
  42. }
  43. void OBSActionRowWidget::setPrefix(QWidget *w, bool auto_connect)
  44. {
  45. setSuffixEnabled(false);
  46. _prefix = w;
  47. if (auto_connect)
  48. this->connectBuddyWidget(w);
  49. _prefix->setParent(this);
  50. layout->addWidget(_prefix, 0, 0, Qt::AlignLeft);
  51. layout->setColumnStretch(0, 3);
  52. }
  53. void OBSActionRowWidget::setSuffix(QWidget *w, bool auto_connect)
  54. {
  55. setPrefixEnabled(false);
  56. _suffix = w;
  57. if (auto_connect)
  58. this->connectBuddyWidget(w);
  59. _suffix->setParent(this);
  60. layout->addWidget(_suffix, 0, 2, Qt::AlignRight | Qt::AlignVCenter);
  61. }
  62. void OBSActionRowWidget::setPrefixEnabled(bool enabled)
  63. {
  64. if (!_prefix)
  65. return;
  66. if (enabled)
  67. setSuffixEnabled(false);
  68. if (enabled == _prefix->isEnabled() && enabled == _prefix->isVisible())
  69. return;
  70. layout->setColumnStretch(0, enabled ? 3 : 0);
  71. _prefix->setEnabled(enabled);
  72. _prefix->setVisible(enabled);
  73. }
  74. void OBSActionRowWidget::setSuffixEnabled(bool enabled)
  75. {
  76. if (!_suffix)
  77. return;
  78. if (enabled)
  79. setPrefixEnabled(false);
  80. if (enabled == _suffix->isEnabled() && enabled == _suffix->isVisible())
  81. return;
  82. _suffix->setEnabled(enabled);
  83. _suffix->setVisible(enabled);
  84. }
  85. void OBSActionRowWidget::setTitle(QString name)
  86. {
  87. nameLabel->setText(name);
  88. setAccessibleName(name);
  89. showTitle(true);
  90. }
  91. void OBSActionRowWidget::setDescription(QString description)
  92. {
  93. descriptionLabel->setText(description);
  94. setAccessibleDescription(description);
  95. showDescription(true);
  96. }
  97. void OBSActionRowWidget::showTitle(bool visible)
  98. {
  99. nameLabel->setVisible(visible);
  100. }
  101. void OBSActionRowWidget::showDescription(bool visible)
  102. {
  103. descriptionLabel->setVisible(visible);
  104. }
  105. void OBSActionRowWidget::setBuddy(QWidget *widget)
  106. {
  107. buddyWidget = widget;
  108. OBSIdianUtils::addClass(widget, "row-buddy");
  109. }
  110. void OBSActionRowWidget::setChangeCursor(bool change)
  111. {
  112. changeCursor = change;
  113. OBSIdianUtils::toggleClass("cursor-pointer", change);
  114. }
  115. void OBSActionRowWidget::enterEvent(QEnterEvent *event)
  116. {
  117. if (!isEnabled())
  118. return;
  119. if (changeCursor) {
  120. setCursor(Qt::PointingHandCursor);
  121. }
  122. OBSIdianUtils::addClass("hover");
  123. if (buddyWidget)
  124. OBSIdianUtils::repolish(buddyWidget);
  125. if (hasPrefix() || hasSuffix()) {
  126. OBSIdianUtils::polishChildren();
  127. }
  128. OBSActionRow::enterEvent(event);
  129. }
  130. void OBSActionRowWidget::leaveEvent(QEvent *event)
  131. {
  132. OBSIdianUtils::removeClass("hover");
  133. if (buddyWidget)
  134. OBSIdianUtils::repolish(buddyWidget);
  135. if (hasPrefix() || hasSuffix()) {
  136. OBSIdianUtils::polishChildren();
  137. }
  138. OBSActionRow::leaveEvent(event);
  139. }
  140. void OBSActionRowWidget::mouseReleaseEvent(QMouseEvent *event)
  141. {
  142. if (event->button() & Qt::LeftButton) {
  143. emit clicked();
  144. }
  145. QFrame::mouseReleaseEvent(event);
  146. }
  147. void OBSActionRowWidget::keyReleaseEvent(QKeyEvent *event)
  148. {
  149. if (event->key() == Qt::Key_Space || event->key() == Qt::Key_Enter) {
  150. emit clicked();
  151. }
  152. QFrame::keyReleaseEvent(event);
  153. }
  154. void OBSActionRowWidget::connectBuddyWidget(QWidget *widget)
  155. {
  156. setAccessibleName(nameLabel->text());
  157. setFocusProxy(widget);
  158. setBuddy(widget);
  159. /* If element is an OBSToggleSwitch and checkable, forward
  160. * clicks to the widget */
  161. OBSToggleSwitch *obsToggle = dynamic_cast<OBSToggleSwitch *>(widget);
  162. if (obsToggle && obsToggle->isCheckable()) {
  163. setChangeCursor(true);
  164. connect(this, &OBSActionRowWidget::clicked, obsToggle, &OBSToggleSwitch::click);
  165. return;
  166. }
  167. /* If element is any other QAbstractButton subclass,
  168. * and checkable, forward clicks to the widget. */
  169. QAbstractButton *button = dynamic_cast<QAbstractButton *>(widget);
  170. if (button && button->isCheckable()) {
  171. setChangeCursor(true);
  172. connect(this, &OBSActionRowWidget::clicked, button, &QAbstractButton::click);
  173. return;
  174. }
  175. /* If element is an OBSComboBox, clicks toggle the dropdown. */
  176. OBSComboBox *obsCombo = dynamic_cast<OBSComboBox *>(widget);
  177. if (obsCombo) {
  178. setChangeCursor(true);
  179. connect(this, &OBSActionRowWidget::clicked, obsCombo, &OBSComboBox::togglePopup);
  180. return;
  181. }
  182. }
  183. /*
  184. * Button for expanding a collapsible ActionRow
  185. */
  186. OBSActionRowExpandButton::OBSActionRowExpandButton(QWidget *parent) : QAbstractButton(parent), OBSIdianUtils(this)
  187. {
  188. setCheckable(true);
  189. }
  190. void OBSActionRowExpandButton::paintEvent(QPaintEvent *)
  191. {
  192. QStyleOptionButton opt;
  193. opt.initFrom(this);
  194. QPainter p(this);
  195. bool checked = isChecked();
  196. opt.state.setFlag(QStyle::State_On, checked);
  197. opt.state.setFlag(QStyle::State_Off, !checked);
  198. opt.state.setFlag(QStyle::State_Sunken, checked);
  199. p.setRenderHint(QPainter::Antialiasing, true);
  200. p.setRenderHint(QPainter::SmoothPixmapTransform, true);
  201. style()->drawPrimitive(QStyle::PE_PanelButtonCommand, &opt, &p, this);
  202. style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &opt, &p, this);
  203. }
  204. /*
  205. * ActionRow variant that can be expanded to show another properties list
  206. */
  207. OBSCollapsibleRowWidget::OBSCollapsibleRowWidget(const QString &name, QWidget *parent) : OBSActionRow(parent)
  208. {
  209. layout = new QVBoxLayout;
  210. layout->setContentsMargins(0, 0, 0, 0);
  211. layout->setSpacing(0);
  212. setLayout(layout);
  213. rowWidget = new OBSCollapsibleRowFrame();
  214. rowLayout = new QHBoxLayout();
  215. rowLayout->setContentsMargins(0, 0, 0, 0);
  216. rowLayout->setSpacing(0);
  217. rowWidget->setLayout(rowLayout);
  218. actionRow = new OBSActionRowWidget();
  219. actionRow->setTitle(name);
  220. actionRow->setChangeCursor(false);
  221. rowLayout->addWidget(actionRow);
  222. propertyList = new OBSPropertiesList(this);
  223. propertyList->setVisible(false);
  224. expandFrame = new QFrame();
  225. btnLayout = new QHBoxLayout();
  226. btnLayout->setContentsMargins(0, 0, 0, 0);
  227. btnLayout->setSpacing(0);
  228. expandFrame->setLayout(btnLayout);
  229. OBSIdianUtils::addClass(expandFrame, "btn-frame");
  230. actionRow->setBuddy(expandFrame);
  231. expandButton = new OBSActionRowExpandButton(this);
  232. btnLayout->addWidget(expandButton);
  233. rowLayout->addWidget(expandFrame);
  234. layout->addWidget(rowWidget);
  235. layout->addWidget(propertyList);
  236. actionRow->setFocusProxy(expandButton);
  237. connect(expandButton, &QAbstractButton::clicked, this, &OBSCollapsibleRowWidget::toggleVisibility);
  238. connect(actionRow, &OBSActionRowWidget::clicked, expandButton, &QAbstractButton::click);
  239. }
  240. OBSCollapsibleRowWidget::OBSCollapsibleRowWidget(const QString &name, const QString &desc, QWidget *parent)
  241. : OBSCollapsibleRowWidget(name, parent)
  242. {
  243. actionRow->setDescription(desc);
  244. }
  245. void OBSCollapsibleRowWidget::setCheckable(bool check)
  246. {
  247. checkable = check;
  248. if (checkable && !toggleSwitch) {
  249. propertyList->setEnabled(false);
  250. OBSIdianUtils::polishChildren(propertyList);
  251. toggleSwitch = new OBSToggleSwitch(false);
  252. actionRow->setSuffix(toggleSwitch, false);
  253. connect(toggleSwitch, &OBSToggleSwitch::toggled, propertyList, &OBSPropertiesList::setEnabled);
  254. }
  255. if (!checkable && toggleSwitch) {
  256. propertyList->setEnabled(true);
  257. OBSIdianUtils::polishChildren(propertyList);
  258. actionRow->suffix()->deleteLater();
  259. }
  260. }
  261. void OBSCollapsibleRowWidget::toggleVisibility()
  262. {
  263. bool visible = !propertyList->isVisible();
  264. propertyList->setVisible(visible);
  265. expandButton->setChecked(visible);
  266. }
  267. void OBSCollapsibleRowWidget::addRow(OBSActionRow *actionRow)
  268. {
  269. propertyList->addRow(actionRow);
  270. }
  271. OBSCollapsibleRowFrame::OBSCollapsibleRowFrame(QWidget *parent) : QFrame(parent), OBSIdianUtils(this) {}
  272. void OBSCollapsibleRowFrame::enterEvent(QEnterEvent *event)
  273. {
  274. setCursor(Qt::PointingHandCursor);
  275. OBSIdianUtils::addClass("hover");
  276. OBSIdianUtils::polishChildren();
  277. QWidget::enterEvent(event);
  278. }
  279. void OBSCollapsibleRowFrame::leaveEvent(QEvent *event)
  280. {
  281. OBSIdianUtils::removeClass("hover");
  282. OBSIdianUtils::polishChildren();
  283. QWidget::leaveEvent(event);
  284. }