abstract_button.cpp 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. // This file is part of Desktop App Toolkit,
  2. // a set of libraries for developing nice desktop applications.
  3. //
  4. // For license and copyright information please follow this link:
  5. // https://github.com/desktop-app/legal/blob/master/LEGAL
  6. //
  7. #include "ui/abstract_button.h"
  8. #include "ui/ui_utility.h"
  9. #include "ui/qt_weak_factory.h"
  10. #include "ui/integration.h"
  11. #include <QtGui/QtEvents>
  12. #include <rpl/filter.h>
  13. #include <rpl/mappers.h>
  14. namespace Ui {
  15. AbstractButton::AbstractButton(QWidget *parent) : RpWidget(parent) {
  16. setMouseTracking(true);
  17. using namespace rpl::mappers;
  18. shownValue()
  19. | rpl::filter(_1 == false)
  20. | rpl::start_with_next([this] { clearState(); }, lifetime());
  21. }
  22. void AbstractButton::leaveEventHook(QEvent *e) {
  23. if (_state & StateFlag::Down) {
  24. return;
  25. }
  26. setOver(false, StateChangeSource::ByHover);
  27. return TWidget::leaveEventHook(e);
  28. }
  29. void AbstractButton::enterEventHook(QEnterEvent *e) {
  30. checkIfOver(mapFromGlobal(QCursor::pos()));
  31. return TWidget::enterEventHook(e);
  32. }
  33. void AbstractButton::setAcceptBoth(bool acceptBoth) {
  34. _acceptBoth = acceptBoth;
  35. }
  36. void AbstractButton::checkIfOver(QPoint localPos) {
  37. auto over = rect().marginsRemoved(getMargins()).contains(localPos);
  38. setOver(over, StateChangeSource::ByHover);
  39. }
  40. void AbstractButton::mousePressEvent(QMouseEvent *e) {
  41. checkIfOver(e->pos());
  42. if (_state & StateFlag::Over) {
  43. const auto set = setDown(
  44. true,
  45. StateChangeSource::ByPress,
  46. e->modifiers(),
  47. e->button());
  48. if (set) {
  49. e->accept();
  50. }
  51. }
  52. }
  53. void AbstractButton::mouseMoveEvent(QMouseEvent *e) {
  54. if (rect().marginsRemoved(getMargins()).contains(e->pos())) {
  55. setOver(true, StateChangeSource::ByHover);
  56. } else {
  57. setOver(false, StateChangeSource::ByHover);
  58. }
  59. }
  60. void AbstractButton::mouseReleaseEvent(QMouseEvent *e) {
  61. const auto set = setDown(
  62. false,
  63. StateChangeSource::ByPress,
  64. e->modifiers(),
  65. e->button());
  66. if (set) {
  67. e->accept();
  68. }
  69. }
  70. void AbstractButton::clicked(
  71. Qt::KeyboardModifiers modifiers,
  72. Qt::MouseButton button) {
  73. _modifiers = modifiers;
  74. const auto weak = MakeWeak(this);
  75. if (button == Qt::LeftButton) {
  76. if (const auto callback = _clickedCallback) {
  77. callback();
  78. }
  79. }
  80. if (weak) {
  81. _clicks.fire_copy(button);
  82. }
  83. }
  84. void AbstractButton::setPointerCursor(bool enablePointerCursor) {
  85. if (_enablePointerCursor != enablePointerCursor) {
  86. _enablePointerCursor = enablePointerCursor;
  87. updateCursor();
  88. }
  89. }
  90. void AbstractButton::setOver(bool over, StateChangeSource source) {
  91. if (over == isOver()) {
  92. return;
  93. }
  94. const auto was = _state;
  95. if (over) {
  96. _state |= StateFlag::Over;
  97. Integration::Instance().registerLeaveSubscription(this);
  98. } else {
  99. _state &= ~State(StateFlag::Over);
  100. Integration::Instance().unregisterLeaveSubscription(this);
  101. }
  102. onStateChanged(was, source);
  103. updateCursor();
  104. update();
  105. }
  106. bool AbstractButton::setDown(
  107. bool down,
  108. StateChangeSource source,
  109. Qt::KeyboardModifiers modifiers,
  110. Qt::MouseButton button) {
  111. if (down
  112. && !(_state & StateFlag::Down)
  113. && (_acceptBoth || button == Qt::LeftButton)) {
  114. auto was = _state;
  115. _state |= StateFlag::Down;
  116. onStateChanged(was, source);
  117. return true;
  118. } else if (!down && (_state & StateFlag::Down)) {
  119. const auto was = _state;
  120. _state &= ~State(StateFlag::Down);
  121. const auto weak = MakeWeak(this);
  122. onStateChanged(was, source);
  123. if (weak) {
  124. if (was & StateFlag::Over) {
  125. clicked(modifiers, button);
  126. } else {
  127. setOver(false, source);
  128. }
  129. }
  130. return true;
  131. }
  132. return false;
  133. }
  134. void AbstractButton::updateCursor() {
  135. const auto pointerCursor = _enablePointerCursor && isOver();
  136. if (_pointerCursor != pointerCursor) {
  137. _pointerCursor = pointerCursor;
  138. setCursor(_pointerCursor ? style::cur_pointer : style::cur_default);
  139. }
  140. }
  141. void AbstractButton::setDisabled(bool disabled) {
  142. auto was = _state;
  143. if (disabled && !(_state & StateFlag::Disabled)) {
  144. _state |= StateFlag::Disabled;
  145. onStateChanged(was, StateChangeSource::ByUser);
  146. } else if (!disabled && (_state & StateFlag::Disabled)) {
  147. _state &= ~State(StateFlag::Disabled);
  148. onStateChanged(was, StateChangeSource::ByUser);
  149. }
  150. }
  151. void AbstractButton::clearState() {
  152. auto was = _state;
  153. _state = StateFlag::None;
  154. onStateChanged(was, StateChangeSource::ByUser);
  155. }
  156. } // namespace Ui