ToggleSwitch.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  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/ToggleSwitch.hpp>
  15. #include <Idian/moc_ToggleSwitch.cpp>
  16. using idian::ToggleSwitch;
  17. namespace {
  18. QColor blendColors(const QColor &color1, const QColor &color2, float ratio)
  19. {
  20. int r = color1.red() * (1 - ratio) + color2.red() * ratio;
  21. int g = color1.green() * (1 - ratio) + color2.green() * ratio;
  22. int b = color1.blue() * (1 - ratio) + color2.blue() * ratio;
  23. return QColor(r, g, b, 255);
  24. }
  25. } // namespace
  26. ToggleSwitch::ToggleSwitch(QWidget *parent)
  27. : QAbstractButton(parent),
  28. animHandle(new QPropertyAnimation(this, "xpos", this)),
  29. animBgColor(new QPropertyAnimation(this, "blend", this)),
  30. Utils(this)
  31. {
  32. Utils::applyStateStylingEventFilter(this);
  33. offPos = rect().width() / 2 - 18;
  34. onPos = rect().width() / 2 + 18;
  35. xPos = offPos;
  36. margin = 3;
  37. setCheckable(true);
  38. setAccessibleName("ToggleSwitch");
  39. installEventFilter(this);
  40. connect(this, &ToggleSwitch::clicked, this, &ToggleSwitch::onClicked);
  41. connect(animHandle, &QVariantAnimation::valueChanged, this, &ToggleSwitch::updateBackgroundColor);
  42. connect(animBgColor, &QVariantAnimation::valueChanged, this, &ToggleSwitch::updateBackgroundColor);
  43. }
  44. ToggleSwitch::ToggleSwitch(bool defaultState, QWidget *parent) : ToggleSwitch(parent)
  45. {
  46. setChecked(defaultState);
  47. if (defaultState) {
  48. xPos = onPos;
  49. }
  50. }
  51. void ToggleSwitch::animateHandlePosition()
  52. {
  53. animHandle->setStartValue(xPos);
  54. int endPos = onPos;
  55. if ((!isDelayed() && !isChecked()) || (isDelayed() && !pendingStatus))
  56. endPos = offPos;
  57. animHandle->setEndValue(endPos);
  58. animHandle->setDuration(120);
  59. animHandle->start();
  60. }
  61. void ToggleSwitch::updateBackgroundColor()
  62. {
  63. QColor offColor = underMouse() ? backgroundInactiveHover : backgroundInactive;
  64. QColor onColor = underMouse() ? backgroundActiveHover : backgroundActive;
  65. if (!isDelayed()) {
  66. int offset = isChecked() ? 0 : offPos;
  67. blend = (float)(xPos - offset) / (float)(onPos);
  68. }
  69. QColor bg = blendColors(offColor, onColor, blend);
  70. if (!isEnabled())
  71. bg = backgroundInactive;
  72. setStyleSheet("background: " + bg.name());
  73. }
  74. void ToggleSwitch::changeEvent(QEvent *event)
  75. {
  76. if (event->type() == QEvent::EnabledChange) {
  77. Utils::toggleClass("disabled", !isEnabled());
  78. updateBackgroundColor();
  79. }
  80. }
  81. void ToggleSwitch::paintEvent(QPaintEvent *)
  82. {
  83. QStyleOptionButton opt;
  84. opt.initFrom(this);
  85. QPainter p(this);
  86. bool showChecked = isChecked();
  87. if (isDelayed()) {
  88. showChecked = pendingStatus;
  89. }
  90. opt.state.setFlag(QStyle::State_On, showChecked);
  91. opt.state.setFlag(QStyle::State_Off, !showChecked);
  92. opt.state.setFlag(QStyle::State_Sunken, true);
  93. style()->drawPrimitive(QStyle::PE_PanelButtonCommand, &opt, &p, this);
  94. p.setRenderHint(QPainter::Antialiasing, true);
  95. p.setBrush(handleColor);
  96. p.drawEllipse(QRectF(xPos, margin, handleSize, handleSize));
  97. }
  98. void ToggleSwitch::showEvent(QShowEvent *e)
  99. {
  100. margin = (rect().height() - handleSize) / 2;
  101. offPos = margin;
  102. onPos = rect().width() - handleSize - margin;
  103. xPos = isChecked() ? onPos : offPos;
  104. updateBackgroundColor();
  105. style()->polish(this);
  106. QAbstractButton::showEvent(e);
  107. }
  108. void ToggleSwitch::click()
  109. {
  110. if (!isDelayed())
  111. QAbstractButton::click();
  112. if (isChecked() == pendingStatus)
  113. setPending(!isChecked());
  114. }
  115. void ToggleSwitch::onClicked(bool checked)
  116. {
  117. if (delayed)
  118. return;
  119. setPending(checked);
  120. }
  121. void ToggleSwitch::setStatus(bool status)
  122. {
  123. if (status == isChecked() && status == pendingStatus)
  124. return;
  125. pendingStatus = status;
  126. setChecked(status);
  127. if (isChecked()) {
  128. animBgColor->setStartValue(0.0f);
  129. animBgColor->setEndValue(1.0f);
  130. } else {
  131. animBgColor->setStartValue(1.0f);
  132. animBgColor->setEndValue(0.0f);
  133. }
  134. animBgColor->setEasingCurve(QEasingCurve::InOutCubic);
  135. animBgColor->setDuration(240);
  136. animBgColor->start();
  137. }
  138. void ToggleSwitch::setPending(bool pending)
  139. {
  140. pendingStatus = pending;
  141. animateHandlePosition();
  142. if (!isDelayed())
  143. return;
  144. if (pending) {
  145. emit pendingChecked();
  146. } else {
  147. emit pendingUnchecked();
  148. }
  149. }
  150. void ToggleSwitch::setDelayed(bool state)
  151. {
  152. delayed = state;
  153. pendingStatus = isChecked();
  154. }
  155. void ToggleSwitch::enterEvent(QEnterEvent *e)
  156. {
  157. setCursor(Qt::PointingHandCursor);
  158. updateBackgroundColor();
  159. QAbstractButton::enterEvent(e);
  160. }
  161. void ToggleSwitch::leaveEvent(QEvent *e)
  162. {
  163. updateBackgroundColor();
  164. QAbstractButton::leaveEvent(e);
  165. }
  166. void ToggleSwitch::keyReleaseEvent(QKeyEvent *e)
  167. {
  168. if (!isDelayed()) {
  169. QAbstractButton::keyReleaseEvent(e);
  170. return;
  171. }
  172. if (e->key() != Qt::Key_Space) {
  173. return;
  174. }
  175. click();
  176. }
  177. void ToggleSwitch::mouseReleaseEvent(QMouseEvent *e)
  178. {
  179. if (!isDelayed()) {
  180. QAbstractButton::mouseReleaseEvent(e);
  181. return;
  182. }
  183. if (e->button() != Qt::LeftButton) {
  184. return;
  185. }
  186. click();
  187. }
  188. QSize ToggleSwitch::sizeHint() const
  189. {
  190. return QSize(2 * handleSize, handleSize);
  191. }