/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "editor/editor_layer_widget.h" #include "ui/painter.h" #include namespace Editor { namespace { constexpr auto kCacheBackgroundFastTimeout = crl::time(200); constexpr auto kCacheBackgroundFullTimeout = crl::time(1000); constexpr auto kFadeBackgroundDuration = crl::time(200); // Thread: Main. [[nodiscard]] bool IsNightMode() { return (st::windowBg->c.lightnessF() < 0.5); } [[nodiscard]] QColor BlurOverlayColor(bool night) { return QColor(16, 16, 16, night ? 128 : 192); } [[nodiscard]] QImage ProcessBackground(QImage image, bool night) { const auto size = image.size(); auto p = QPainter(&image); p.fillRect( QRect(QPoint(), image.size() / image.devicePixelRatio()), BlurOverlayColor(night)); p.end(); return Images::DitherImage( Images::BlurLargeImage( std::move(image).scaled( size / style::ConvertScale(4), Qt::IgnoreAspectRatio, Qt::SmoothTransformation), 24).scaled( size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); } } // namespace LayerWidget::LayerWidget( not_null parent, base::unique_qptr content) : Ui::LayerWidget(parent) , _content(std::move(content)) , _backgroundTimer([=] { checkCacheBackground(); }) { _content->setParent(this); _content->show(); paintRequest( ) | rpl::start_with_next([=](const QRect &clip) { auto p = QPainter(this); const auto faded = _backgroundFade.value(1.); if (faded < 1.) { p.drawImage(rect(), _backgroundBack); if (faded > 0.) { p.setOpacity(faded); p.drawImage(rect(), _background); } } else { p.drawImage(rect(), _background); } }, lifetime()); } bool LayerWidget::eventHook(QEvent *e) { return RpWidget::eventHook(e); } void LayerWidget::start() { _backgroundNight = IsNightMode(); _background = ProcessBackground(renderBackground(), _backgroundNight); sizeValue( ) | rpl::start_with_next([=](const QSize &size) { checkBackgroundStale(); _content->resize(size); }, lifetime()); style::PaletteChanged() | rpl::start_with_next([=] { checkBackgroundStale(); }, lifetime()); } void LayerWidget::checkBackgroundStale() { const auto ratio = style::DevicePixelRatio(); const auto &ready = _backgroundNext.isNull() ? _background : _backgroundNext; if (ready.size() == size() * ratio && _backgroundNight == IsNightMode()) { _backgroundTimer.cancel(); } else if (!_backgroundCaching && !_backgroundTimer.isActive()) { _lastAreaChangeTime = crl::now(); _backgroundTimer.callOnce(kCacheBackgroundFastTimeout); } } QImage LayerWidget::renderBackground() { const auto parent = parentWidget(); const auto target = parent->parentWidget(); Ui::SendPendingMoveResizeEvents(target); const auto ratio = style::DevicePixelRatio(); auto image = QImage(size() * ratio, QImage::Format_ARGB32_Premultiplied); image.setDevicePixelRatio(ratio); const auto shown = !parent->isHidden(); const auto focused = shown && Ui::InFocusChain(parent); if (shown) { if (focused) { target->setFocus(); } parent->hide(); } auto p = QPainter(&image); Ui::RenderWidget(p, target, QPoint(), geometry()); p.end(); if (shown) { parent->show(); if (focused) { if (isHidden()) { parent->setFocus(); } else { setInnerFocus(); } } } return image; } void LayerWidget::checkCacheBackground() { if (_backgroundCaching || _backgroundTimer.isActive()) { return; } const auto now = crl::now(); if (now - _lastAreaChangeTime < kCacheBackgroundFullTimeout && QGuiApplication::mouseButtons() != 0) { _backgroundTimer.callOnce(kCacheBackgroundFastTimeout); return; } cacheBackground(); } void LayerWidget::cacheBackground() { _backgroundCaching = true; const auto weak = Ui::MakeWeak(this); const auto night = IsNightMode(); crl::async([weak, night, image = renderBackground()]() mutable { auto result = ProcessBackground(image, night); crl::on_main([weak, night, result = std::move(result)]() mutable { if (const auto strong = weak.data()) { strong->backgroundReady(std::move(result), night); } }); }); } void LayerWidget::backgroundReady(QImage background, bool night) { _backgroundCaching = false; const auto required = size() * style::DevicePixelRatio(); if (background.size() == required && night == IsNightMode()) { _backgroundNext = std::move(background); _backgroundNight = night; if (!_backgroundFade.animating()) { startBackgroundFade(); } update(); } else if (_background.size() != required) { _backgroundTimer.callOnce(kCacheBackgroundFastTimeout); } } void LayerWidget::startBackgroundFade() { if (_backgroundNext.isNull()) { return; } _backgroundBack = std::move(_background); _background = base::take(_backgroundNext); _backgroundFade.start([=] { update(); if (!_backgroundFade.animating()) { _backgroundBack = QImage(); startBackgroundFade(); } }, 0., 1., kFadeBackgroundDuration); } void LayerWidget::parentResized() { resizeToWidth(parentWidget()->width()); if (_background.isNull()) { start(); } } void LayerWidget::keyPressEvent(QKeyEvent *e) { QGuiApplication::sendEvent(_content.get(), e); } int LayerWidget::resizeGetHeight(int newWidth) { return parentWidget()->height(); } bool LayerWidget::closeByOutsideClick() const { return false; } } // namespace Editor