221 lines
		
	
	
	
		
			5.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			221 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
 |