Row.cpp 8.8 KB

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