tdesktop/Telegram/SourceFiles/editor/editor_layer_widget.cpp

222 lines
5.4 KiB
C++

/*
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 <QtGui/QGuiApplication>
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<QWidget*> parent,
base::unique_qptr<Ui::RpWidget> 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