695 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			695 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
| This file is part of Telegram Desktop,
 | |
| the official desktop version of Telegram messaging app, see https://telegram.org
 | |
| 
 | |
| Telegram Desktop is free software: you can redistribute it and/or modify
 | |
| it under the terms of the GNU General Public License as published by
 | |
| the Free Software Foundation, either version 3 of the License, or
 | |
| (at your option) any later version.
 | |
| 
 | |
| It is distributed in the hope that it will be useful,
 | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | |
| GNU General Public License for more details.
 | |
| 
 | |
| In addition, as a special exception, the copyright holders give permission
 | |
| to link the code of portions of this program with the OpenSSL library.
 | |
| 
 | |
| Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
 | |
| Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 | |
| */
 | |
| #include "stdafx.h"
 | |
| #include "lang.h"
 | |
| 
 | |
| #include "media/media_clip_reader.h"
 | |
| #include "layerwidget.h"
 | |
| #include "application.h"
 | |
| #include "mainwindow.h"
 | |
| #include "mainwidget.h"
 | |
| #include "ui/filedialog.h"
 | |
| #include "styles/style_stickers.h"
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| constexpr int kStickerPreviewEmojiLimit = 10;
 | |
| 
 | |
| } // namespace
 | |
| 
 | |
| void LayerWidget::setInnerFocus() {
 | |
| 	auto focused = App::wnd()->focusWidget();
 | |
| 	if (!isAncestorOf(focused)) {
 | |
| 		doSetInnerFocus();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| class LayerStackWidget::BackgroundWidget : public TWidget {
 | |
| public:
 | |
| 	BackgroundWidget(QWidget *parent) : TWidget(parent)
 | |
| 	, _shadow(st::boxShadow) {
 | |
| 	}
 | |
| 
 | |
| 	void setLayerBox(const QRect &box, const QRect &hiddenSpecialBox) {
 | |
| 		_box = box;
 | |
| 		_hiddenSpecialBox = hiddenSpecialBox;
 | |
| 		update();
 | |
| 	}
 | |
| 	void setOpacity(float64 opacity) {
 | |
| 		_opacity = opacity;
 | |
| 	}
 | |
| 
 | |
| protected:
 | |
| 	void paintEvent(QPaintEvent *e) override {
 | |
| 		Painter p(this);
 | |
| 
 | |
| 		p.setOpacity(_opacity);
 | |
| 		if (_box.isNull()) {
 | |
| 			p.fillRect(rect(), st::layerBg);
 | |
| 		} else {
 | |
| 			auto clip = QRegion(rect()) - _box;
 | |
| 			for (auto &r : clip.rects()) {
 | |
| 				p.fillRect(r, st::layerBg);
 | |
| 			}
 | |
| 			p.setClipRegion(clip);
 | |
| 			_shadow.paint(p, _box, st::boxShadowShift);
 | |
| 			if (!_hiddenSpecialBox.isNull()) {
 | |
| 				p.setClipRegion(QRegion(rect()) - _hiddenSpecialBox);
 | |
| 				_shadow.paint(p, _hiddenSpecialBox, st::boxShadowShift);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| private:
 | |
| 	QRect _box, _hiddenSpecialBox;
 | |
| 	float64 _opacity = 0.;
 | |
| 
 | |
| 	Ui::RectShadow _shadow;
 | |
| 
 | |
| };
 | |
| 
 | |
| LayerStackWidget::LayerStackWidget(QWidget *parent) : TWidget(parent)
 | |
| , _background(this)
 | |
| , a_bg(0)
 | |
| , a_layer(0)
 | |
| , _a_background(animation(this, &LayerStackWidget::step_background)) {
 | |
| 	setGeometry(parentWidget()->rect());
 | |
| 	hide();
 | |
| }
 | |
| 
 | |
| void LayerStackWidget::paintEvent(QPaintEvent *e) {
 | |
| 	if (!layer() && !_specialLayer && _layerCache.isNull()) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (!_layerCache.isNull()) {
 | |
| 		Painter p(this);
 | |
| 		p.setClipRect(rect());
 | |
| 		p.setOpacity(a_layer.current());
 | |
| 		if (!_hiddenSpecialLayerCache.isNull()) {
 | |
| 			p.drawPixmap(_hiddenSpecialLayerCacheBox.topLeft(), _hiddenSpecialLayerCache);
 | |
| 		}
 | |
| 		p.drawPixmap(_layerCacheBox.topLeft(), _layerCache);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void LayerStackWidget::keyPressEvent(QKeyEvent *e) {
 | |
| 	if (e->key() == Qt::Key_Escape) {
 | |
| 		onCloseCurrent();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void LayerStackWidget::mousePressEvent(QMouseEvent *e) {
 | |
| 	onCloseCurrent();
 | |
| }
 | |
| 
 | |
| void LayerStackWidget::onCloseCurrent() {
 | |
| 	if (layer()) {
 | |
| 		onCloseLayers();
 | |
| 	} else {
 | |
| 		onClose();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void LayerStackWidget::onCloseLayers() {
 | |
| 	if (_specialLayer) {
 | |
| 		clearLayers();
 | |
| 		fixOrder();
 | |
| 		if (App::wnd()) App::wnd()->setInnerFocus();
 | |
| 	} else {
 | |
| 		onClose();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void LayerStackWidget::onClose() {
 | |
| 	startHide();
 | |
| }
 | |
| 
 | |
| void LayerStackWidget::onLayerClosed(LayerWidget *l) {
 | |
| 	l->deleteLater();
 | |
| 	if (l == _specialLayer) {
 | |
| 		onClose();
 | |
| 		_specialLayer = nullptr;
 | |
| 	} else if (l == layer()) {
 | |
| 		_layers.pop_back();
 | |
| 		if (auto newLayer = layer()) {
 | |
| 			l->hide();
 | |
| 			newLayer->parentResized();
 | |
| 			if (!_a_background.animating()) {
 | |
| 				newLayer->show();
 | |
| 			}
 | |
| 		} else if (_specialLayer) {
 | |
| 			l->hide();
 | |
| 		} else {
 | |
| 			_layers.push_back(l); // For animation cache grab.
 | |
| 			onClose();
 | |
| 			_layers.pop_back();
 | |
| 		}
 | |
| 		fixOrder();
 | |
| 		if (App::wnd()) App::wnd()->setInnerFocus();
 | |
| 		updateLayerBox();
 | |
| 		sendFakeMouseEvent();
 | |
| 	} else {
 | |
| 		for (auto i = _layers.begin(), e = _layers.end(); i != e; ++i) {
 | |
| 			if (l == *i) {
 | |
| 				_layers.erase(i);
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void LayerStackWidget::onLayerResized() {
 | |
| 	updateLayerBox();
 | |
| }
 | |
| 
 | |
| void LayerStackWidget::updateLayerBox() {
 | |
| 	auto getLayerBox = [this]() {
 | |
| 		if (!_layerCache.isNull()) {
 | |
| 			return _layerCacheBox;
 | |
| 		} else if (auto l = layer()) {
 | |
| 			return l->geometry();
 | |
| 		} else if (_specialLayer) {
 | |
| 			return _specialLayer->geometry();
 | |
| 		}
 | |
| 		return QRect();
 | |
| 	};
 | |
| 	auto getSpecialLayerBox = [this]() {
 | |
| 		if (!_layerCache.isNull()) {
 | |
| 			return _hiddenSpecialLayerCacheBox;
 | |
| 		} else if (auto l = layer()) {
 | |
| 			return _specialLayer ? _specialLayer->geometry() : QRect();
 | |
| 		}
 | |
| 		return QRect();
 | |
| 	};
 | |
| 	_background->setLayerBox(getLayerBox(), getSpecialLayerBox());
 | |
| 	update();
 | |
| }
 | |
| 
 | |
| void LayerStackWidget::startShow() {
 | |
| 	startAnimation(1);
 | |
| 	show();
 | |
| }
 | |
| 
 | |
| void LayerStackWidget::showFast() {
 | |
| 	if (_a_background.animating()) {
 | |
| 		_a_background.step(getms() + st::layerSlideDuration + 1);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void LayerStackWidget::startHide() {
 | |
| 	if (isHidden() || _hiding) {
 | |
| 		return;
 | |
| 	}
 | |
| 	_hiding = true;
 | |
| 	startAnimation(0);
 | |
| }
 | |
| 
 | |
| void LayerStackWidget::startAnimation(float64 toOpacity) {
 | |
| 	if (App::app()) App::app()->mtpPause();
 | |
| 	a_bg.start(toOpacity);
 | |
| 	a_layer.start(toOpacity);
 | |
| 	_a_background.start();
 | |
| 	if (_layerCache.isNull()) {
 | |
| 		if (auto cacheLayer = layer() ? layer() : _specialLayer.ptr()) {
 | |
| 			_layerCache = myGrab(cacheLayer);
 | |
| 			_layerCacheBox = cacheLayer->geometry();
 | |
| 			if (layer() && _specialLayer) {
 | |
| 				_hiddenSpecialLayerCache = myGrab(_specialLayer);
 | |
| 				_hiddenSpecialLayerCacheBox = _specialLayer->geometry();
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	if (_specialLayer) {
 | |
| 		_specialLayer->hide();
 | |
| 	}
 | |
| 	if (auto l = layer()) {
 | |
| 		l->hide();
 | |
| 	}
 | |
| 	updateLayerBox();
 | |
| 	if (App::wnd()) App::wnd()->setInnerFocus();
 | |
| }
 | |
| 
 | |
| bool LayerStackWidget::canSetFocus() const {
 | |
| 	return (layer() || _specialLayer) && !_hiding;
 | |
| }
 | |
| 
 | |
| void LayerStackWidget::setInnerFocus() {
 | |
| 	if (_a_background.animating()) {
 | |
| 		setFocus();
 | |
| 	} else if (auto l = layer()) {
 | |
| 		l->setInnerFocus();
 | |
| 	} else if (_specialLayer) {
 | |
| 		_specialLayer->setInnerFocus();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| bool LayerStackWidget::contentOverlapped(const QRect &globalRect) {
 | |
| 	if (isHidden()) {
 | |
| 		return false;
 | |
| 	}
 | |
| 	if (_specialLayer && _specialLayer->overlaps(globalRect)) {
 | |
| 		return true;
 | |
| 	}
 | |
| 	if (auto l = layer()) {
 | |
| 		return l->overlaps(globalRect);
 | |
| 	}
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| void LayerStackWidget::resizeEvent(QResizeEvent *e) {
 | |
| 	_background->setGeometry(rect());
 | |
| 	if (_specialLayer) {
 | |
| 		_specialLayer->parentResized();
 | |
| 	}
 | |
| 	if (auto l = layer()) {
 | |
| 		l->parentResized();
 | |
| 	}
 | |
| 	updateLayerBox();
 | |
| }
 | |
| 
 | |
| void LayerStackWidget::showLayer(LayerWidget *l) {
 | |
| 	clearLayers();
 | |
| 	appendLayer(l);
 | |
| }
 | |
| 
 | |
| void LayerStackWidget::showSpecialLayer(LayerWidget *l) {
 | |
| 	clearLayers();
 | |
| 	if (_specialLayer) {
 | |
| 		_specialLayer.destroyDelayed();
 | |
| 	}
 | |
| 	_specialLayer = l;
 | |
| 	activateLayer(l);
 | |
| }
 | |
| 
 | |
| void LayerStackWidget::appendLayer(LayerWidget *l) {
 | |
| 	if (auto oldLayer = layer()) {
 | |
| 		oldLayer->hide();
 | |
| 	}
 | |
| 	_layers.push_back(l);
 | |
| 	activateLayer(l);
 | |
| }
 | |
| 
 | |
| void LayerStackWidget::prependLayer(LayerWidget *l) {
 | |
| 	if (_layers.empty()) {
 | |
| 		showLayer(l);
 | |
| 	} else {
 | |
| 		l->hide();
 | |
| 		_layers.push_front(l);
 | |
| 		initChildLayer(l);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void LayerStackWidget::clearLayers() {
 | |
| 	for_const (auto oldLayer, _layers) {
 | |
| 		oldLayer->hide();
 | |
| 		oldLayer->deleteLater();
 | |
| 	}
 | |
| 	_layers.clear();
 | |
| 	updateLayerBox();
 | |
| 	sendFakeMouseEvent();
 | |
| }
 | |
| 
 | |
| void LayerStackWidget::initChildLayer(LayerWidget *l) {
 | |
| 	l->setParent(this);
 | |
| 	connect(l, SIGNAL(closed(LayerWidget*)), this, SLOT(onLayerClosed(LayerWidget*)));
 | |
| 	connect(l, SIGNAL(resized()), this, SLOT(onLayerResized()));
 | |
| 	connect(l, SIGNAL(destroyed(QObject*)), this, SLOT(onLayerDestroyed(QObject*)));
 | |
| 	l->parentResized();
 | |
| 	fixOrder();
 | |
| }
 | |
| 
 | |
| void LayerStackWidget::activateLayer(LayerWidget *l) {
 | |
| 	initChildLayer(l);
 | |
| 	if (isHidden()) {
 | |
| 		startShow();
 | |
| 	} else {
 | |
| 		l->show();
 | |
| 		l->showDone();
 | |
| 		if (App::wnd()) App::wnd()->setInnerFocus();
 | |
| 		updateLayerBox();
 | |
| 	}
 | |
| 	fixOrder();
 | |
| 	sendFakeMouseEvent();
 | |
| }
 | |
| 
 | |
| void LayerStackWidget::fixOrder() {
 | |
| 	if (auto l = layer()) {
 | |
| 		_background->raise();
 | |
| 		l->raise();
 | |
| 	} else if (_specialLayer) {
 | |
| 		_specialLayer->raise();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void LayerStackWidget::sendFakeMouseEvent() {
 | |
| 	sendSynteticMouseEvent(this, QEvent::MouseMove, Qt::NoButton);
 | |
| }
 | |
| 
 | |
| void LayerStackWidget::step_background(float64 ms, bool timer) {
 | |
| 	float64 dt = ms / (_hiding ? st::layerHideDuration : st::layerSlideDuration);
 | |
| 	if (dt >= 1) {
 | |
| 		a_bg.finish();
 | |
| 		a_layer.finish();
 | |
| 		_a_background.stop();
 | |
| 		_layerCache = _hiddenSpecialLayerCache = QPixmap();
 | |
| 		if (_hiding) {
 | |
| 			App::wnd()->layerFinishedHide(this);
 | |
| 		} else {
 | |
| 			if (_specialLayer) {
 | |
| 				_specialLayer->show();
 | |
| 				_specialLayer->showDone();
 | |
| 			}
 | |
| 			if (auto l = layer()) {
 | |
| 				l->show();
 | |
| 				l->showDone();
 | |
| 			}
 | |
| 			fixOrder();
 | |
| 			if (App::wnd()) App::wnd()->setInnerFocus();
 | |
| 		}
 | |
| 		updateLayerBox();
 | |
| 		if (App::app()) App::app()->mtpUnpause();
 | |
| 	} else {
 | |
| 		a_bg.update(dt, anim::easeOutCirc);
 | |
| 		a_layer.update(dt, anim::linear);
 | |
| 	}
 | |
| 	_background->setOpacity(a_bg.current());
 | |
| 	if (timer) {
 | |
| 		_background->update();
 | |
| 		update();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void LayerStackWidget::onLayerDestroyed(QObject *obj) {
 | |
| 	if (obj == _specialLayer) {
 | |
| 		_specialLayer = nullptr;
 | |
| 		onClose();
 | |
| 	} else if (obj == layer()) {
 | |
| 		_layers.pop_back();
 | |
| 		if (auto newLayer = layer()) {
 | |
| 			newLayer->parentResized();
 | |
| 			if (!_a_background.animating()) {
 | |
| 				newLayer->show();
 | |
| 			}
 | |
| 		} else if (!_specialLayer) {
 | |
| 			onClose();
 | |
| 		}
 | |
| 		fixOrder();
 | |
| 		if (App::wnd()) App::wnd()->setInnerFocus();
 | |
| 		updateLayerBox();
 | |
| 	} else {
 | |
| 		for (auto i = _layers.begin(), e = _layers.end(); i != e; ++i) {
 | |
| 			if (obj == *i) {
 | |
| 				_layers.erase(i);
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| LayerStackWidget::~LayerStackWidget() {
 | |
| 	if (App::wnd()) App::wnd()->noLayerStack(this);
 | |
| }
 | |
| 
 | |
| MediaPreviewWidget::MediaPreviewWidget(QWidget *parent) : TWidget(parent)
 | |
| , a_shown(0, 0)
 | |
| , _a_shown(animation(this, &MediaPreviewWidget::step_shown))
 | |
| , _emojiSize(EmojiSizes[EIndex + 1] / cIntRetinaFactor()) {
 | |
| 	setAttribute(Qt::WA_TransparentForMouseEvents);
 | |
| 	subscribe(FileDownload::ImageLoaded(), [this] { update(); });
 | |
| }
 | |
| 
 | |
| void MediaPreviewWidget::paintEvent(QPaintEvent *e) {
 | |
| 	Painter p(this);
 | |
| 	QRect r(e->rect());
 | |
| 
 | |
| 	auto image = currentImage();
 | |
| 	int w = image.width() / cIntRetinaFactor(), h = image.height() / cIntRetinaFactor();
 | |
| 	if (_a_shown.animating()) {
 | |
| 		float64 shown = a_shown.current();
 | |
| 		p.setOpacity(shown);
 | |
| //		w = qMax(qRound(w * (st::stickerPreviewMin + ((1. - st::stickerPreviewMin) * shown)) / 2.) * 2 + int(w % 2), 1);
 | |
| //		h = qMax(qRound(h * (st::stickerPreviewMin + ((1. - st::stickerPreviewMin) * shown)) / 2.) * 2 + int(h % 2), 1);
 | |
| 	}
 | |
| 	p.fillRect(r, st::stickerPreviewBg);
 | |
| 	p.drawPixmap((width() - w) / 2, (height() - h) / 2, image);
 | |
| 	if (!_emojiList.isEmpty()) {
 | |
| 		int emojiCount = _emojiList.size();
 | |
| 		int emojiWidth = (emojiCount * _emojiSize) + (emojiCount - 1) * st::stickerEmojiSkip;
 | |
| 		int emojiLeft = (width() - emojiWidth) / 2;
 | |
| 		int esize = EmojiSizes[EIndex + 1];
 | |
| 		for_const (auto emoji, _emojiList) {
 | |
| 			p.drawPixmapLeft(emojiLeft, (height() - h) / 2 - (_emojiSize * 2), width(), App::emojiLarge(), QRect(emoji->x * esize, emoji->y * esize, esize, esize));
 | |
| 			emojiLeft += _emojiSize + st::stickerEmojiSkip;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void MediaPreviewWidget::resizeEvent(QResizeEvent *e) {
 | |
| 	update();
 | |
| }
 | |
| 
 | |
| void MediaPreviewWidget::step_shown(float64 ms, bool timer) {
 | |
| 	float64 dt = ms / st::stickerPreviewDuration;
 | |
| 	if (dt >= 1) {
 | |
| 		_a_shown.stop();
 | |
| 		a_shown.finish();
 | |
| 		if (a_shown.current() < 0.5) hide();
 | |
| 	} else {
 | |
| 		a_shown.update(dt, anim::linear);
 | |
| 	}
 | |
| 	if (timer) update();
 | |
| }
 | |
| 
 | |
| void MediaPreviewWidget::showPreview(DocumentData *document) {
 | |
| 	if (!document || (!document->isAnimation() && !document->sticker())) {
 | |
| 		hidePreview();
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	startShow();
 | |
| 	_photo = nullptr;
 | |
| 	_document = document;
 | |
| 	fillEmojiString();
 | |
| 	resetGifAndCache();
 | |
| }
 | |
| 
 | |
| void MediaPreviewWidget::showPreview(PhotoData *photo) {
 | |
| 	if (!photo || photo->full->isNull()) {
 | |
| 		hidePreview();
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	startShow();
 | |
| 	_photo = photo;
 | |
| 	_document = nullptr;
 | |
| 	resetGifAndCache();
 | |
| }
 | |
| 
 | |
| void MediaPreviewWidget::startShow() {
 | |
| 	_cache = QPixmap();
 | |
| 	if (isHidden() || _a_shown.animating()) {
 | |
| 		if (isHidden()) show();
 | |
| 		a_shown.start(1);
 | |
| 		_a_shown.start();
 | |
| 	} else {
 | |
| 		update();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void MediaPreviewWidget::hidePreview() {
 | |
| 	if (isHidden()) {
 | |
| 		return;
 | |
| 	}
 | |
| 	if (_gif) _cache = currentImage();
 | |
| 	a_shown.start(0);
 | |
| 	_a_shown.start();
 | |
| 	_photo = nullptr;
 | |
| 	_document = nullptr;
 | |
| 	resetGifAndCache();
 | |
| }
 | |
| 
 | |
| void MediaPreviewWidget::fillEmojiString() {
 | |
| 	auto getStickerEmojiList = [this](uint64 setId) {
 | |
| 		QList<EmojiPtr> result;
 | |
| 		auto &sets = Global::StickerSets();
 | |
| 		auto it = sets.constFind(setId);
 | |
| 		if (it == sets.cend()) {
 | |
| 			return result;
 | |
| 		}
 | |
| 		for (auto i = it->emoji.cbegin(), e = it->emoji.cend(); i != e; ++i) {
 | |
| 			for_const (auto document, *i) {
 | |
| 				if (document == _document) {
 | |
| 					result.append(i.key());
 | |
| 					if (result.size() >= kStickerPreviewEmojiLimit) {
 | |
| 						return result;
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		return result;
 | |
| 	};
 | |
| 
 | |
| 	if (auto sticker = _document->sticker()) {
 | |
| 		auto &inputSet = sticker->set;
 | |
| 		if (inputSet.type() == mtpc_inputStickerSetID) {
 | |
| 			_emojiList = getStickerEmojiList(inputSet.c_inputStickerSetID().vid.v);
 | |
| 		} else {
 | |
| 			_emojiList.clear();
 | |
| 			if (auto emoji = emojiFromText(sticker->alt)) {
 | |
| 				_emojiList.append(emoji);
 | |
| 			}
 | |
| 		}
 | |
| 	} else {
 | |
| 		_emojiList.clear();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void MediaPreviewWidget::resetGifAndCache() {
 | |
| 	_gif.reset();
 | |
| 	_cacheStatus = CacheNotLoaded;
 | |
| 	_cachedSize = QSize();
 | |
| }
 | |
| 
 | |
| QSize MediaPreviewWidget::currentDimensions() const {
 | |
| 	if (!_cachedSize.isEmpty()) {
 | |
| 		return _cachedSize;
 | |
| 	}
 | |
| 	if (!_document && !_photo) {
 | |
| 		_cachedSize = QSize(_cache.width() / cIntRetinaFactor(), _cache.height() / cIntRetinaFactor());
 | |
| 		return _cachedSize;
 | |
| 	}
 | |
| 
 | |
| 	QSize result, box;
 | |
| 	if (_photo) {
 | |
| 		result = QSize(_photo->full->width(), _photo->full->height());
 | |
| 		box = QSize(width() - 2 * st::boxVerticalMargin, height() - 2 * st::boxVerticalMargin);
 | |
| 	} else {
 | |
| 		result = _document->dimensions;
 | |
| 		if (_gif && _gif->ready()) {
 | |
| 			result = QSize(_gif->width(), _gif->height());
 | |
| 		}
 | |
| 		if (_document->sticker()) {
 | |
| 			box = QSize(st::maxStickerSize, st::maxStickerSize);
 | |
| 		} else {
 | |
| 			box = QSize(2 * st::maxStickerSize, 2 * st::maxStickerSize);
 | |
| 		}
 | |
| 	}
 | |
| 	result = QSize(qMax(convertScale(result.width()), 1), qMax(convertScale(result.height()), 1));
 | |
| 	if (result.width() > box.width()) {
 | |
| 		result.setHeight(qMax((box.width() * result.height()) / result.width(), 1));
 | |
| 		result.setWidth(box.width());
 | |
| 	}
 | |
| 	if (result.height() > box.height()) {
 | |
| 		result.setWidth(qMax((box.height() * result.width()) / result.height(), 1));
 | |
| 		result.setHeight(box.height());
 | |
| 	}
 | |
| 	if (_photo) {
 | |
| 		_cachedSize = result;
 | |
| 	}
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| QPixmap MediaPreviewWidget::currentImage() const {
 | |
| 	if (_document) {
 | |
| 		if (_document->sticker()) {
 | |
| 			if (_cacheStatus != CacheLoaded) {
 | |
| 				_document->checkSticker();
 | |
| 				if (_document->sticker()->img->isNull()) {
 | |
| 					if (_cacheStatus != CacheThumbLoaded && _document->thumb->loaded()) {
 | |
| 						QSize s = currentDimensions();
 | |
| 						_cache = _document->thumb->pixBlurred(s.width(), s.height());
 | |
| 						_cacheStatus = CacheThumbLoaded;
 | |
| 					}
 | |
| 				} else {
 | |
| 					QSize s = currentDimensions();
 | |
| 					_cache = _document->sticker()->img->pix(s.width(), s.height());
 | |
| 					_cacheStatus = CacheLoaded;
 | |
| 				}
 | |
| 			}
 | |
| 		} else {
 | |
| 			_document->automaticLoad(nullptr);
 | |
| 			if (_document->loaded()) {
 | |
| 				if (!_gif && !_gif.isBad()) {
 | |
| 					auto that = const_cast<MediaPreviewWidget*>(this);
 | |
| 					that->_gif = Media::Clip::MakeReader(_document->location(), _document->data(), [this, that](Media::Clip::Notification notification) {
 | |
| 						that->clipCallback(notification);
 | |
| 					});
 | |
| 					if (_gif) _gif->setAutoplay();
 | |
| 				}
 | |
| 			}
 | |
| 			if (_gif && _gif->started()) {
 | |
| 				QSize s = currentDimensions();
 | |
| 				return _gif->current(s.width(), s.height(), s.width(), s.height(), getms());
 | |
| 			}
 | |
| 			if (_cacheStatus != CacheThumbLoaded && _document->thumb->loaded()) {
 | |
| 				QSize s = currentDimensions();
 | |
| 				_cache = _document->thumb->pixBlurred(s.width(), s.height());
 | |
| 				_cacheStatus = CacheThumbLoaded;
 | |
| 			}
 | |
| 		}
 | |
| 	} else if (_photo) {
 | |
| 		if (_cacheStatus != CacheLoaded) {
 | |
| 			if (_photo->full->loaded()) {
 | |
| 				QSize s = currentDimensions();
 | |
| 				_cache = _photo->full->pix(s.width(), s.height());
 | |
| 				_cacheStatus = CacheLoaded;
 | |
| 			} else {
 | |
| 				if (_cacheStatus != CacheThumbLoaded && _photo->thumb->loaded()) {
 | |
| 					QSize s = currentDimensions();
 | |
| 					_cache = _photo->thumb->pixBlurred(s.width(), s.height());
 | |
| 					_cacheStatus = CacheThumbLoaded;
 | |
| 				}
 | |
| 				_photo->thumb->load();
 | |
| 				_photo->full->load();
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 	}
 | |
| 	return _cache;
 | |
| }
 | |
| 
 | |
| void MediaPreviewWidget::clipCallback(Media::Clip::Notification notification) {
 | |
| 	using namespace Media::Clip;
 | |
| 	switch (notification) {
 | |
| 	case NotificationReinit: {
 | |
| 		if (_gif && _gif->state() == State::Error) {
 | |
| 			_gif.setBad();
 | |
| 		}
 | |
| 
 | |
| 		if (_gif && _gif->ready() && !_gif->started()) {
 | |
| 			QSize s = currentDimensions();
 | |
| 			_gif->start(s.width(), s.height(), s.width(), s.height(), ImageRoundRadius::None);
 | |
| 		}
 | |
| 
 | |
| 		update();
 | |
| 	} break;
 | |
| 
 | |
| 	case NotificationRepaint: {
 | |
| 		if (_gif && !_gif->currentDisplayed()) {
 | |
| 			update();
 | |
| 		}
 | |
| 	} break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| MediaPreviewWidget::~MediaPreviewWidget() {
 | |
| }
 | 
