animations.cpp 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  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/effects/animations.h"
  8. #include "base/invoke_queued.h"
  9. #include "ui/ui_utility.h"
  10. #include "styles/style_basic.h"
  11. #include <QtCore/QPointer>
  12. #include <crl/crl_on_main.h>
  13. #include <crl/crl.h>
  14. #include <rpl/filter.h>
  15. #include <range/v3/algorithm/remove_if.hpp>
  16. #include <range/v3/algorithm/remove.hpp>
  17. #include <range/v3/algorithm/find.hpp>
  18. namespace Ui {
  19. namespace Animations {
  20. namespace {
  21. constexpr auto kAnimationTick = crl::time(1000) / st::universalDuration;
  22. constexpr auto kIgnoreUpdatesTimeout = crl::time(4);
  23. Manager *ManagerInstance = nullptr;
  24. } // namespace
  25. void Basic::start() {
  26. Expects(ManagerInstance != nullptr);
  27. if (animating()) {
  28. restart();
  29. } else {
  30. ManagerInstance->start(this);
  31. }
  32. }
  33. void Basic::stop() {
  34. Expects(ManagerInstance != nullptr);
  35. if (animating()) {
  36. ManagerInstance->stop(this);
  37. }
  38. }
  39. void Basic::restart() {
  40. Expects(_started >= 0);
  41. _started = crl::now();
  42. Ensures(_started >= 0);
  43. }
  44. void Basic::markStarted() {
  45. Expects(_started < 0);
  46. _started = crl::now();
  47. Ensures(_started >= 0);
  48. }
  49. void Basic::markStopped() {
  50. Expects(_started >= 0);
  51. _started = -1;
  52. }
  53. Manager::Manager() {
  54. Expects(ManagerInstance == nullptr);
  55. ManagerInstance = this;
  56. crl::on_main_update_requests(
  57. ) | rpl::filter([=] {
  58. return (_lastUpdateTime + kIgnoreUpdatesTimeout < crl::now());
  59. }) | rpl::start_with_next([=] {
  60. update();
  61. }, _lifetime);
  62. }
  63. Manager::~Manager() {
  64. Expects(ManagerInstance == this);
  65. Expects(_active.empty());
  66. Expects(_starting.empty());
  67. ManagerInstance = nullptr;
  68. }
  69. void Manager::start(not_null<Basic*> animation) {
  70. _forceImmediateUpdate = true;
  71. if (_updating) {
  72. _starting.emplace_back(animation.get());
  73. } else {
  74. schedule();
  75. _active.emplace_back(animation.get());
  76. }
  77. }
  78. void Manager::stop(not_null<Basic*> animation) {
  79. if (empty(_active) && empty(_starting)) {
  80. return;
  81. }
  82. const auto value = animation.get();
  83. const auto proj = &ActiveBasicPointer::get;
  84. auto &list = _updating ? _starting : _active;
  85. list.erase(ranges::remove(list, value, proj), end(list));
  86. if (_updating) {
  87. const auto i = ranges::find(_active, value, proj);
  88. if (i != end(_active)) {
  89. *i = nullptr;
  90. _removedWhileUpdating = true;
  91. }
  92. } else if (empty(_active)) {
  93. stopTimer();
  94. }
  95. }
  96. void Manager::update() {
  97. if (_active.empty() || _updating || _scheduled) {
  98. return;
  99. }
  100. const auto now = crl::now();
  101. if (_forceImmediateUpdate) {
  102. _forceImmediateUpdate = false;
  103. }
  104. schedule();
  105. _updating = true;
  106. const auto guard = gsl::finally([&] { _updating = false; });
  107. _lastUpdateTime = now;
  108. const auto isFinished = [&](const ActiveBasicPointer &element) {
  109. return !element.call(now);
  110. };
  111. _active.erase(ranges::remove_if(_active, isFinished), end(_active));
  112. if (_removedWhileUpdating) {
  113. _removedWhileUpdating = false;
  114. const auto proj = &ActiveBasicPointer::get;
  115. _active.erase(ranges::remove(_active, nullptr, proj), end(_active));
  116. }
  117. if (!empty(_starting)) {
  118. _active.insert(
  119. end(_active),
  120. std::make_move_iterator(begin(_starting)),
  121. std::make_move_iterator(end(_starting)));
  122. _starting.clear();
  123. }
  124. }
  125. void Manager::updateQueued() {
  126. Expects(_timerId == 0);
  127. _timerId = -1;
  128. InvokeQueued(delayedCallGuard(), [=] {
  129. Expects(_timerId < 0);
  130. _timerId = 0;
  131. update();
  132. });
  133. }
  134. void Manager::schedule() {
  135. if (_scheduled || _timerId < 0) {
  136. return;
  137. }
  138. stopTimer();
  139. _scheduled = true;
  140. PostponeCall(delayedCallGuard(), [=] {
  141. _scheduled = false;
  142. if (_active.empty()) {
  143. return;
  144. }
  145. if (_forceImmediateUpdate) {
  146. _forceImmediateUpdate = false;
  147. updateQueued();
  148. } else {
  149. const auto next = _lastUpdateTime + kAnimationTick;
  150. const auto now = crl::now();
  151. if (now < next) {
  152. _timerId = startTimer(next - now, Qt::PreciseTimer);
  153. } else {
  154. updateQueued();
  155. }
  156. }
  157. });
  158. }
  159. not_null<const QObject*> Manager::delayedCallGuard() const {
  160. return static_cast<const QObject*>(this);
  161. }
  162. void Manager::stopTimer() {
  163. if (_timerId > 0) {
  164. killTimer(base::take(_timerId));
  165. }
  166. }
  167. void Manager::timerEvent(QTimerEvent *e) {
  168. update();
  169. }
  170. } // namespace Animations
  171. } // namespace Ui