editor_layer_widget.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "editor/editor_layer_widget.h"
  8. #include "ui/painter.h"
  9. #include "ui/ui_utility.h"
  10. #include <QtGui/QGuiApplication>
  11. namespace Editor {
  12. namespace {
  13. constexpr auto kCacheBackgroundFastTimeout = crl::time(200);
  14. constexpr auto kCacheBackgroundFullTimeout = crl::time(1000);
  15. constexpr auto kFadeBackgroundDuration = crl::time(200);
  16. // Thread: Main.
  17. [[nodiscard]] bool IsNightMode() {
  18. return (st::windowBg->c.lightnessF() < 0.5);
  19. }
  20. [[nodiscard]] QColor BlurOverlayColor(bool night) {
  21. return QColor(16, 16, 16, night ? 128 : 192);
  22. }
  23. [[nodiscard]] QImage ProcessBackground(QImage image, bool night) {
  24. const auto size = image.size();
  25. auto p = QPainter(&image);
  26. p.fillRect(
  27. QRect(QPoint(), image.size() / image.devicePixelRatio()),
  28. BlurOverlayColor(night));
  29. p.end();
  30. return Images::DitherImage(
  31. Images::BlurLargeImage(
  32. std::move(image).scaled(
  33. size / style::ConvertScale(4),
  34. Qt::IgnoreAspectRatio,
  35. Qt::SmoothTransformation),
  36. 24).scaled(
  37. size,
  38. Qt::IgnoreAspectRatio,
  39. Qt::SmoothTransformation));
  40. }
  41. } // namespace
  42. LayerWidget::LayerWidget(
  43. not_null<QWidget*> parent,
  44. base::unique_qptr<Ui::RpWidget> content)
  45. : Ui::LayerWidget(parent)
  46. , _content(std::move(content))
  47. , _backgroundTimer([=] { checkCacheBackground(); }) {
  48. _content->setParent(this);
  49. _content->show();
  50. paintRequest(
  51. ) | rpl::start_with_next([=](const QRect &clip) {
  52. auto p = QPainter(this);
  53. const auto faded = _backgroundFade.value(1.);
  54. if (faded < 1.) {
  55. p.drawImage(rect(), _backgroundBack);
  56. if (faded > 0.) {
  57. p.setOpacity(faded);
  58. p.drawImage(rect(), _background);
  59. }
  60. } else {
  61. p.drawImage(rect(), _background);
  62. }
  63. }, lifetime());
  64. }
  65. bool LayerWidget::eventHook(QEvent *e) {
  66. return RpWidget::eventHook(e);
  67. }
  68. void LayerWidget::start() {
  69. _backgroundNight = IsNightMode();
  70. _background = ProcessBackground(renderBackground(), _backgroundNight);
  71. sizeValue(
  72. ) | rpl::start_with_next([=](const QSize &size) {
  73. checkBackgroundStale();
  74. _content->resize(size);
  75. }, lifetime());
  76. style::PaletteChanged() | rpl::start_with_next([=] {
  77. checkBackgroundStale();
  78. }, lifetime());
  79. }
  80. void LayerWidget::checkBackgroundStale() {
  81. const auto ratio = style::DevicePixelRatio();
  82. const auto &ready = _backgroundNext.isNull()
  83. ? _background
  84. : _backgroundNext;
  85. if (ready.size() == size() * ratio
  86. && _backgroundNight == IsNightMode()) {
  87. _backgroundTimer.cancel();
  88. } else if (!_backgroundCaching && !_backgroundTimer.isActive()) {
  89. _lastAreaChangeTime = crl::now();
  90. _backgroundTimer.callOnce(kCacheBackgroundFastTimeout);
  91. }
  92. }
  93. QImage LayerWidget::renderBackground() {
  94. const auto parent = parentWidget();
  95. const auto target = parent->parentWidget();
  96. Ui::SendPendingMoveResizeEvents(target);
  97. const auto ratio = style::DevicePixelRatio();
  98. auto image = QImage(size() * ratio, QImage::Format_ARGB32_Premultiplied);
  99. image.setDevicePixelRatio(ratio);
  100. const auto shown = !parent->isHidden();
  101. const auto focused = shown && Ui::InFocusChain(parent);
  102. if (shown) {
  103. if (focused) {
  104. target->setFocus();
  105. }
  106. parent->hide();
  107. }
  108. auto p = QPainter(&image);
  109. Ui::RenderWidget(p, target, QPoint(), geometry());
  110. p.end();
  111. if (shown) {
  112. parent->show();
  113. if (focused) {
  114. if (isHidden()) {
  115. parent->setFocus();
  116. } else {
  117. setInnerFocus();
  118. }
  119. }
  120. }
  121. return image;
  122. }
  123. void LayerWidget::checkCacheBackground() {
  124. if (_backgroundCaching || _backgroundTimer.isActive()) {
  125. return;
  126. }
  127. const auto now = crl::now();
  128. if (now - _lastAreaChangeTime < kCacheBackgroundFullTimeout
  129. && QGuiApplication::mouseButtons() != 0) {
  130. _backgroundTimer.callOnce(kCacheBackgroundFastTimeout);
  131. return;
  132. }
  133. cacheBackground();
  134. }
  135. void LayerWidget::cacheBackground() {
  136. _backgroundCaching = true;
  137. const auto weak = Ui::MakeWeak(this);
  138. const auto night = IsNightMode();
  139. crl::async([weak, night, image = renderBackground()]() mutable {
  140. auto result = ProcessBackground(image, night);
  141. crl::on_main([weak, night, result = std::move(result)]() mutable {
  142. if (const auto strong = weak.data()) {
  143. strong->backgroundReady(std::move(result), night);
  144. }
  145. });
  146. });
  147. }
  148. void LayerWidget::backgroundReady(QImage background, bool night) {
  149. _backgroundCaching = false;
  150. const auto required = size() * style::DevicePixelRatio();
  151. if (background.size() == required && night == IsNightMode()) {
  152. _backgroundNext = std::move(background);
  153. _backgroundNight = night;
  154. if (!_backgroundFade.animating()) {
  155. startBackgroundFade();
  156. }
  157. update();
  158. } else if (_background.size() != required) {
  159. _backgroundTimer.callOnce(kCacheBackgroundFastTimeout);
  160. }
  161. }
  162. void LayerWidget::startBackgroundFade() {
  163. if (_backgroundNext.isNull()) {
  164. return;
  165. }
  166. _backgroundBack = std::move(_background);
  167. _background = base::take(_backgroundNext);
  168. _backgroundFade.start([=] {
  169. update();
  170. if (!_backgroundFade.animating()) {
  171. _backgroundBack = QImage();
  172. startBackgroundFade();
  173. }
  174. }, 0., 1., kFadeBackgroundDuration);
  175. }
  176. void LayerWidget::parentResized() {
  177. resizeToWidth(parentWidget()->width());
  178. if (_background.isNull()) {
  179. start();
  180. }
  181. }
  182. void LayerWidget::keyPressEvent(QKeyEvent *e) {
  183. QGuiApplication::sendEvent(_content.get(), e);
  184. }
  185. int LayerWidget::resizeGetHeight(int newWidth) {
  186. return parentWidget()->height();
  187. }
  188. bool LayerWidget::closeByOutsideClick() const {
  189. return false;
  190. }
  191. } // namespace Editor