tdesktop/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp
John Preston 376941dd5e Mixed channel access hash to the game score encrypted data.
Added new lang strings for playing game send actions (not done yet).
Also moved alot of icons from sprite to separate b&w files.
2016-09-30 15:52:03 +03:00

1362 lines
47 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 "inline_bots/inline_bot_layout_internal.h"
#include "styles/style_overview.h"
#include "styles/style_history.h"
#include "inline_bots/inline_bot_result.h"
#include "media/media_clip_reader.h"
#include "history/history_location_manager.h"
#include "localstorage.h"
#include "mainwidget.h"
#include "lang.h"
#include "playerwidget.h"
namespace InlineBots {
namespace Layout {
namespace internal {
FileBase::FileBase(Result *result) : ItemBase(result) {
}
FileBase::FileBase(DocumentData *document) : ItemBase(document) {
}
DocumentData *FileBase::getShownDocument() const {
if (DocumentData *result = getDocument()) {
return result;
}
return getResultDocument();
}
int FileBase::content_width() const {
DocumentData *document = getShownDocument();
if (document->dimensions.width() > 0) {
return document->dimensions.width();
}
if (!document->thumb->isNull()) {
return convertScale(document->thumb->width());
}
return 0;
}
int FileBase::content_height() const {
DocumentData *document = getShownDocument();
if (document->dimensions.height() > 0) {
return document->dimensions.height();
}
if (!document->thumb->isNull()) {
return convertScale(document->thumb->height());
}
return 0;
}
int FileBase::content_duration() const {
if (DocumentData *document = getShownDocument()) {
if (document->duration() > 0) {
return document->duration();
} else if (SongData *song = document->song()) {
if (song->duration) {
return song->duration;
}
}
}
return getResultDuration();
}
ImagePtr FileBase::content_thumb() const {
if (DocumentData *document = getShownDocument()) {
if (!document->thumb->isNull()) {
return document->thumb;
}
}
return getResultThumb();
}
Gif::Gif(Result *result) : FileBase(result) {
}
Gif::Gif(DocumentData *document, bool hasDeleteButton) : FileBase(document)
, _delete(hasDeleteButton ? new DeleteSavedGifClickHandler(document) : nullptr) {
}
void Gif::initDimensions() {
int32 w = content_width(), h = content_height();
if (w <= 0 || h <= 0) {
_maxw = 0;
} else {
w = w * st::inlineMediaHeight / h;
_maxw = qMax(w, int32(st::inlineResultsMinWidth));
}
_minh = st::inlineMediaHeight + st::inlineResultsSkip;
}
void Gif::setPosition(int32 position) {
ItemBase::setPosition(position);
if (_position < 0) {
_gif.reset();
}
}
void DeleteSavedGifClickHandler::onClickImpl() const {
int32 index = cSavedGifs().indexOf(_data);
if (index >= 0) {
cRefSavedGifs().remove(index);
Local::writeSavedGifs();
MTP::send(MTPmessages_SaveGif(_data->mtpInput(), MTP_bool(true)));
}
if (App::main()) emit App::main()->savedGifsUpdated();
}
void Gif::paint(Painter &p, const QRect &clip, const PaintContext *context) const {
DocumentData *document = getShownDocument();
document->automaticLoad(nullptr);
bool loaded = document->loaded(), loading = document->loading(), displayLoading = document->displayLoading();
if (loaded && !_gif && !_gif.isBad()) {
auto that = const_cast<Gif*>(this);
that->_gif = Media::Clip::MakeReader(document->location(), document->data(), [that](Media::Clip::Notification notification) {
that->clipCallback(notification);
});
if (_gif) _gif->setAutoplay();
}
bool animating = (_gif && _gif->started());
if (displayLoading) {
ensureAnimation();
if (!_animation->radial.animating()) {
_animation->radial.start(document->progress());
}
}
bool radial = isRadialAnimation(context->ms);
int32 height = st::inlineMediaHeight;
QSize frame = countFrameSize();
QRect r(0, 0, _width, height);
if (animating) {
if (!_thumb.isNull()) _thumb = QPixmap();
p.drawPixmap(r.topLeft(), _gif->current(frame.width(), frame.height(), _width, height, context->paused ? 0 : context->ms));
} else {
prepareThumb(_width, height, frame);
if (_thumb.isNull()) {
p.fillRect(r, st::overviewPhotoBg);
} else {
p.drawPixmap(r.topLeft(), _thumb);
}
}
if (radial || _gif.isBad() || (!_gif && !loaded && !loading)) {
auto radialOpacity = (radial && loaded) ? _animation->radial.opacity() : 1.;
if (_animation && _animation->_a_over.animating(context->ms)) {
auto over = _animation->_a_over.current();
p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over));
p.fillRect(r, st::black);
} else {
p.fillRect(r, (_state & StateFlag::Over) ? st::msgDateImgBgOver : st::msgDateImgBg);
}
p.setOpacity(radialOpacity * p.opacity());
p.setOpacity(radialOpacity);
auto icon = ([loaded, radial, loading] {
if (loaded && !radial) {
return &st::historyFileInPlay;
} else if (radial || loading) {
return &st::historyFileInCancel;
}
return &st::historyFileInDownload;
})();
QRect inner((_width - st::msgFileSize) / 2, (height - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize);
icon->paintInCenter(p, inner);
if (radial) {
p.setOpacity(1);
QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
_animation->radial.draw(p, rinner, st::msgFileRadialLine, st::msgInBg);
}
}
if (_delete && (_state & StateFlag::Over)) {
float64 deleteOver = _a_deleteOver.current(context->ms, (_state & StateFlag::DeleteOver) ? 1 : 0);
QPoint deletePos = QPoint(_width - st::stickerPanDelete.pxWidth(), 0);
p.setOpacity(deleteOver + (1 - deleteOver) * st::stickerPanDeleteOpacity);
p.drawSpriteLeft(deletePos, _width, st::stickerPanDelete);
p.setOpacity(1);
}
}
void Gif::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const {
if (x >= 0 && x < _width && y >= 0 && y < st::inlineMediaHeight) {
if (_delete && (rtl() ? _width - x : x) >= _width - st::stickerPanDelete.pxWidth() && y < st::stickerPanDelete.pxHeight()) {
link = _delete;
} else {
link = _send;
}
}
}
void Gif::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
if (!p) return;
if (_delete && p == _delete) {
bool wasactive = (_state & StateFlag::DeleteOver);
if (active != wasactive) {
auto from = active ? 0. : 1., to = active ? 1. : 0.;
_a_deleteOver.start([this] { update(); }, from, to, st::stickersRowDuration);
if (active) {
_state |= StateFlag::DeleteOver;
} else {
_state &= ~StateFlag::DeleteOver;
}
}
}
if (p == _delete || p == _send) {
bool wasactive = (_state & StateFlag::Over);
if (active != wasactive) {
if (!getShownDocument()->loaded()) {
ensureAnimation();
auto from = active ? 0. : 1., to = active ? 1. : 0.;
_animation->_a_over.start([this] { update(); }, from, to, st::stickersRowDuration);
}
if (active) {
_state |= StateFlag::Over;
} else {
_state &= ~StateFlag::Over;
}
}
}
ItemBase::clickHandlerActiveChanged(p, active);
}
QSize Gif::countFrameSize() const {
bool animating = (_gif && _gif->ready());
int32 framew = animating ? _gif->width() : content_width(), frameh = animating ? _gif->height() : content_height(), height = st::inlineMediaHeight;
if (framew * height > frameh * _width) {
if (framew < st::maxStickerSize || frameh > height) {
if (frameh > height || (framew * height / frameh) <= st::maxStickerSize) {
framew = framew * height / frameh;
frameh = height;
} else {
frameh = int32(frameh * st::maxStickerSize) / framew;
framew = st::maxStickerSize;
}
}
} else {
if (frameh < st::maxStickerSize || framew > _width) {
if (framew > _width || (frameh * _width / framew) <= st::maxStickerSize) {
frameh = frameh * _width / framew;
framew = _width;
} else {
framew = int32(framew * st::maxStickerSize) / frameh;
frameh = st::maxStickerSize;
}
}
}
return QSize(framew, frameh);
}
void Gif::prepareThumb(int32 width, int32 height, const QSize &frame) const {
if (DocumentData *document = getShownDocument()) {
if (!document->thumb->isNull()) {
if (document->thumb->loaded()) {
if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) {
_thumb = document->thumb->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), ImagePixSmooth, width, height);
}
} else {
document->thumb->load();
}
}
} else {
ImagePtr thumb = getResultThumb();
if (!thumb->isNull()) {
if (thumb->loaded()) {
if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) {
_thumb = thumb->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), ImagePixSmooth, width, height);
}
} else {
thumb->load();
}
}
}
}
void Gif::ensureAnimation() const {
if (!_animation) {
_animation = std_::make_unique<AnimationData>(animation(const_cast<Gif*>(this), &Gif::step_radial));
}
}
bool Gif::isRadialAnimation(uint64 ms) const {
if (!_animation || !_animation->radial.animating()) return false;
_animation->radial.step(ms);
return _animation && _animation->radial.animating();
}
void Gif::step_radial(uint64 ms, bool timer) {
if (timer) {
update();
} else {
DocumentData *document = getShownDocument();
_animation->radial.update(document->progress(), !document->loading() || document->loaded(), ms);
if (!_animation->radial.animating() && document->loaded()) {
_animation.reset();
}
}
}
void Gif::clipCallback(Media::Clip::Notification notification) {
using namespace Media::Clip;
switch (notification) {
case NotificationReinit: {
if (_gif) {
if (_gif->state() == State::Error) {
_gif.setBad();
getShownDocument()->forget();
} else if (_gif->ready() && !_gif->started()) {
int32 height = st::inlineMediaHeight;
QSize frame = countFrameSize();
_gif->start(frame.width(), frame.height(), _width, height, ImageRoundRadius::None);
} else if (_gif->autoPausedGif() && !Ui::isInlineItemVisible(this)) {
_gif.reset();
getShownDocument()->forget();
}
}
update();
} break;
case NotificationRepaint: {
if (_gif && !_gif->currentDisplayed()) {
update();
}
} break;
}
}
Sticker::Sticker(Result *result) : FileBase(result) {
}
void Sticker::initDimensions() {
_maxw = st::stickerPanSize.width();
_minh = st::stickerPanSize.height();
}
void Sticker::preload() const {
if (DocumentData *document = getShownDocument()) {
bool goodThumb = !document->thumb->isNull() && ((document->thumb->width() >= 128) || (document->thumb->height() >= 128));
if (goodThumb) {
document->thumb->load();
} else {
document->checkSticker();
}
} else {
ImagePtr thumb = getResultThumb();
if (!thumb->isNull()) {
thumb->load();
}
}
}
void Sticker::paint(Painter &p, const QRect &clip, const PaintContext *context) const {
bool loaded = getShownDocument()->loaded();
auto over = _a_over.current(_active ? 1. : 0.);
if (over > 0) {
p.setOpacity(over);
App::roundRect(p, QRect(QPoint(0, 0), st::stickerPanSize), st::emojiPanHover, StickerHoverCorners);
p.setOpacity(1);
}
prepareThumb();
if (!_thumb.isNull()) {
int w = _thumb.width() / cIntRetinaFactor(), h = _thumb.height() / cIntRetinaFactor();
QPoint pos = QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2);
p.drawPixmap(pos, _thumb);
}
}
void Sticker::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const {
if (x >= 0 && x < _width && y >= 0 && y < st::inlineMediaHeight) {
link = _send;
}
}
void Sticker::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
if (!p) return;
if (p == _send) {
if (active != _active) {
_active = active;
auto from = active ? 0. : 1., to = active ? 1. : 0.;
_a_over.start([this] { update(); }, from, to, st::stickersRowDuration);
}
}
ItemBase::clickHandlerActiveChanged(p, active);
}
QSize Sticker::getThumbSize() const {
int width = qMax(content_width(), 1), height = qMax(content_height(), 1);
float64 coefw = (st::stickerPanSize.width() - st::buttonRadius * 2) / float64(width);
float64 coefh = (st::stickerPanSize.height() - st::buttonRadius * 2) / float64(height);
float64 coef = qMin(qMin(coefw, coefh), 1.);
int w = qRound(coef * content_width()), h = qRound(coef * content_height());
return QSize(qMax(w, 1), qMax(h, 1));
}
void Sticker::prepareThumb() const {
if (DocumentData *document = getShownDocument()) {
bool goodThumb = !document->thumb->isNull() && ((document->thumb->width() >= 128) || (document->thumb->height() >= 128));
if (goodThumb) {
document->thumb->load();
} else {
document->checkSticker();
}
ImagePtr sticker = goodThumb ? document->thumb : document->sticker()->img;
if (!_thumbLoaded && sticker->loaded()) {
QSize thumbSize = getThumbSize();
_thumb = sticker->pix(thumbSize.width(), thumbSize.height());
_thumbLoaded = true;
}
} else {
ImagePtr thumb = getResultThumb();
if (thumb->loaded()) {
if (!_thumbLoaded) {
QSize thumbSize = getThumbSize();
_thumb = thumb->pix(thumbSize.width(), thumbSize.height());
_thumbLoaded = true;
}
} else {
thumb->load();
}
}
}
Photo::Photo(Result *result) : ItemBase(result) {
}
void Photo::initDimensions() {
PhotoData *photo = getShownPhoto();
int32 w = photo->full->width(), h = photo->full->height();
if (w <= 0 || h <= 0) {
_maxw = 0;
} else {
w = w * st::inlineMediaHeight / h;
_maxw = qMax(w, int32(st::inlineResultsMinWidth));
}
_minh = st::inlineMediaHeight + st::inlineResultsSkip;
}
void Photo::paint(Painter &p, const QRect &clip, const PaintContext *context) const {
int32 height = st::inlineMediaHeight;
QSize frame = countFrameSize();
QRect r(0, 0, _width, height);
prepareThumb(_width, height, frame);
if (_thumb.isNull()) {
p.fillRect(r, st::overviewPhotoBg);
} else {
p.drawPixmap(r.topLeft(), _thumb);
}
}
void Photo::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const {
if (x >= 0 && x < _width && y >= 0 && y < st::inlineMediaHeight) {
link = _send;
}
}
PhotoData *Photo::getShownPhoto() const {
if (PhotoData *result = getPhoto()) {
return result;
}
return getResultPhoto();
}
QSize Photo::countFrameSize() const {
PhotoData *photo = getShownPhoto();
int32 framew = photo->full->width(), frameh = photo->full->height(), height = st::inlineMediaHeight;
if (framew * height > frameh * _width) {
if (framew < st::maxStickerSize || frameh > height) {
if (frameh > height || (framew * height / frameh) <= st::maxStickerSize) {
framew = framew * height / frameh;
frameh = height;
} else {
frameh = int32(frameh * st::maxStickerSize) / framew;
framew = st::maxStickerSize;
}
}
} else {
if (frameh < st::maxStickerSize || framew > _width) {
if (framew > _width || (frameh * _width / framew) <= st::maxStickerSize) {
frameh = frameh * _width / framew;
framew = _width;
} else {
framew = int32(framew * st::maxStickerSize) / frameh;
frameh = st::maxStickerSize;
}
}
}
return QSize(framew, frameh);
}
void Photo::prepareThumb(int32 width, int32 height, const QSize &frame) const {
if (PhotoData *photo = getShownPhoto()) {
if (photo->medium->loaded()) {
if (!_thumbLoaded || _thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) {
_thumb = photo->medium->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), ImagePixSmooth, width, height);
}
_thumbLoaded = true;
} else {
if (photo->thumb->loaded()) {
if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) {
_thumb = photo->thumb->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), ImagePixSmooth, width, height);
}
}
photo->medium->load();
}
} else {
ImagePtr thumb = getResultThumb();
if (thumb->loaded()) {
if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) {
_thumb = thumb->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), ImagePixSmooth, width, height);
}
} else {
thumb->load();
}
}
}
Video::Video(Result *result) : FileBase(result)
, _link(getResultContentUrlHandler())
, _title(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip)
, _description(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip) {
if (int duration = content_duration()) {
_duration = formatDurationText(duration);
_durationWidth = st::normalFont->width(_duration);
}
}
void Video::initDimensions() {
bool withThumb = !content_thumb()->isNull();
_maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft;
int32 textWidth = _maxw - (withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : 0);
TextParseOptions titleOpts = { 0, _maxw, 2 * st::semiboldFont->height, Qt::LayoutDirectionAuto };
QString title = textOneLine(_result->getLayoutTitle());
if (title.isEmpty()) {
title = lang(lng_media_video);
}
_title.setText(st::semiboldFont, title, titleOpts);
int32 titleHeight = qMin(_title.countHeight(_maxw), 2 * st::semiboldFont->height);
int32 descriptionLines = withThumb ? (titleHeight > st::semiboldFont->height ? 1 : 2) : 3;
TextParseOptions descriptionOpts = { TextParseMultiline, _maxw, descriptionLines * st::normalFont->height, Qt::LayoutDirectionAuto };
QString description = _result->getLayoutDescription();
if (description.isEmpty()) {
description = _duration;
}
_description.setText(st::normalFont, description, descriptionOpts);
int32 descriptionHeight = qMin(_description.countHeight(_maxw), descriptionLines * st::normalFont->height);
_minh = st::inlineThumbSize;
_minh += st::inlineRowMargin * 2 + st::inlineRowBorder;
}
void Video::paint(Painter &p, const QRect &clip, const PaintContext *context) const {
int left = st::inlineThumbSize + st::inlineThumbSkip;
bool withThumb = !content_thumb()->isNull();
if (withThumb) {
prepareThumb(st::inlineThumbSize, st::inlineThumbSize);
if (_thumb.isNull()) {
p.fillRect(rtlrect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize, _width), st::overviewPhotoBg);
} else {
p.drawPixmapLeft(0, st::inlineRowMargin, _width, _thumb);
}
} else {
p.fillRect(rtlrect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize, _width), st::black);
}
if (!_duration.isEmpty()) {
int durationTop = st::inlineRowMargin + st::inlineThumbSize - st::normalFont->height - st::inlineDurationMargin;
int durationW = _durationWidth + 2 * st::msgDateImgPadding.x(), durationH = st::normalFont->height + 2 * st::msgDateImgPadding.y();
int durationX = (st::inlineThumbSize - durationW) / 2, durationY = st::inlineRowMargin + st::inlineThumbSize - durationH;
App::roundRect(p, durationX, durationY - st::msgDateImgPadding.y(), durationW, durationH, st::msgDateImgBg, DateCorners);
p.setPen(st::white);
p.setFont(st::normalFont);
p.drawText(durationX + st::msgDateImgPadding.x(), durationTop + st::normalFont->ascent, _duration);
}
p.setPen(st::black);
_title.drawLeftElided(p, left, st::inlineRowMargin, _width - left, _width, 2);
int32 titleHeight = qMin(_title.countHeight(_width - left), st::semiboldFont->height * 2);
p.setPen(st::inlineDescriptionFg);
int32 descriptionLines = withThumb ? (titleHeight > st::semiboldFont->height ? 1 : 2) : 3;
_description.drawLeftElided(p, left, st::inlineRowMargin + titleHeight, _width - left, _width, descriptionLines);
if (!context->lastRow) {
p.fillRect(rtlrect(left, _height - st::inlineRowBorder, _width - left, st::inlineRowBorder, _width), st::inlineRowBorderFg);
}
}
void Video::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const {
if (x >= 0 && x < st::inlineThumbSize && y >= st::inlineRowMargin && y < st::inlineRowMargin + st::inlineThumbSize) {
link = _link;
return;
}
if (x >= st::inlineThumbSize + st::inlineThumbSkip && x < _width && y >= 0 && y < _height) {
link = _send;
return;
}
}
void Video::prepareThumb(int32 width, int32 height) const {
ImagePtr thumb = content_thumb();
if (thumb->loaded()) {
if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) {
int32 w = qMax(convertScale(thumb->width()), 1), h = qMax(convertScale(thumb->height()), 1);
if (w * height > h * width) {
if (height < h) {
w = w * height / h;
h = height;
}
} else {
if (width < w) {
h = h * width / w;
w = width;
}
}
_thumb = thumb->pixNoCache(w * cIntRetinaFactor(), h * cIntRetinaFactor(), ImagePixSmooth, width, height);
}
} else {
thumb->load();
}
}
void OpenFileClickHandler::onClickImpl() const {
_result->openFile();
}
void CancelFileClickHandler::onClickImpl() const {
_result->cancelFile();
}
File::File(Result *result) : FileBase(result)
, _title(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::msgFileSize - st::inlineThumbSkip)
, _description(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::msgFileSize - st::inlineThumbSkip)
, _open(new OpenFileClickHandler(result))
, _cancel(new CancelFileClickHandler(result)) {
updateStatusText();
regDocumentItem(getShownDocument(), this);
}
void File::initDimensions() {
_maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft;
int textWidth = _maxw - (st::msgFileSize + st::inlineThumbSkip);
TextParseOptions titleOpts = { 0, _maxw, st::semiboldFont->height, Qt::LayoutDirectionAuto };
_title.setText(st::semiboldFont, textOneLine(_result->getLayoutTitle()), titleOpts);
TextParseOptions descriptionOpts = { TextParseMultiline, _maxw, st::normalFont->height, Qt::LayoutDirectionAuto };
_description.setText(st::normalFont, _result->getLayoutDescription(), descriptionOpts);
_minh = st::msgFileSize;
_minh += st::inlineRowMargin * 2 + st::inlineRowBorder;
}
void File::paint(Painter &p, const QRect &clip, const PaintContext *context) const {
int32 left = st::msgFileSize + st::inlineThumbSkip;
DocumentData *document = getShownDocument();
bool loaded = document->loaded(), displayLoading = document->displayLoading();
if (displayLoading) {
ensureAnimation();
if (!_animation->radial.animating()) {
_animation->radial.start(document->progress());
}
}
bool showPause = updateStatusText();
bool radial = isRadialAnimation(context->ms);
auto inner = rtlrect(0, st::inlineRowMargin, st::msgFileSize, st::msgFileSize, _width);
p.setPen(Qt::NoPen);
if (isThumbAnimation(context->ms)) {
float64 over = _animation->a_thumbOver.current();
p.setBrush(style::interpolate(st::msgFileInBg, st::msgFileInBgOver, over));
} else {
bool over = ClickHandler::showAsActive(document->loading() ? _cancel : _open);
p.setBrush((over ? st::msgFileInBgOver : st::msgFileInBg));
}
p.setRenderHint(QPainter::HighQualityAntialiasing);
p.drawEllipse(inner);
p.setRenderHint(QPainter::HighQualityAntialiasing, false);
if (radial) {
auto radialCircle = inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine));
_animation->radial.draw(p, radialCircle, st::msgFileRadialLine, st::msgInBg);
}
auto icon = ([showPause, radial, document] {
if (showPause) {
return &st::historyFileInPause;
} else if (radial || document->loading()) {
return &st::historyFileInCancel;
} else if (true || document->loaded()) {
if (document->isImage()) {
return &st::historyFileInImage;
} else if (document->voice() || document->song()) {
return &st::historyFileInPlay;
}
return &st::historyFileInDocument;
}
return &st::historyFileInDownload;
})();
icon->paintInCenter(p, inner);
int titleTop = st::inlineRowMargin + st::inlineRowFileNameTop;
int descriptionTop = st::inlineRowMargin + st::inlineRowFileDescriptionTop;
p.setPen(st::black);
_title.drawLeftElided(p, left, titleTop, _width - left, _width);
p.setPen(st::inlineDescriptionFg);
bool drawStatusSize = true;
if (_statusSize == FileStatusSizeReady || _statusSize == FileStatusSizeLoaded || _statusSize == FileStatusSizeFailed) {
if (!_description.isEmpty()) {
_description.drawLeftElided(p, left, descriptionTop, _width - left, _width);
drawStatusSize = false;
}
}
if (drawStatusSize) {
p.setFont(st::normalFont);
p.drawTextLeft(left, descriptionTop, _width, _statusText);
}
if (!context->lastRow) {
p.fillRect(rtlrect(left, _height - st::inlineRowBorder, _width - left, st::inlineRowBorder, _width), st::inlineRowBorderFg);
}
}
void File::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const {
if (x >= 0 && x < st::msgFileSize && y >= st::inlineRowMargin && y < st::inlineRowMargin + st::msgFileSize) {
link = getShownDocument()->loading() ? _cancel : _open;
return;
}
if (x >= st::msgFileSize + st::inlineThumbSkip && x < _width && y >= 0 && y < _height) {
link = _send;
return;
}
}
void File::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
if (p == _open || p == _cancel) {
if (active) {
ensureAnimation();
_animation->a_thumbOver.start(1);
} else {
if (!_animation) {
ensureAnimation();
_animation->a_thumbOver = anim::fvalue(1, 1);
}
_animation->a_thumbOver.start(0);
}
_animation->_a_thumbOver.start();
}
}
File::~File() {
unregDocumentItem(getShownDocument(), this);
}
void File::step_thumbOver(float64 ms, bool timer) {
float64 dt = ms / st::msgFileOverDuration;
if (dt >= 1) {
_animation->a_thumbOver.finish();
_animation->_a_thumbOver.stop();
checkAnimationFinished();
} else if (!timer) {
_animation->a_thumbOver.update(dt, anim::linear);
}
if (timer) {
Ui::repaintInlineItem(this);
}
}
void File::step_radial(uint64 ms, bool timer) {
if (timer) {
Ui::repaintInlineItem(this);
} else {
DocumentData *document = getShownDocument();
_animation->radial.update(document->progress(), !document->loading() || document->loaded(), ms);
if (!_animation->radial.animating()) {
checkAnimationFinished();
}
}
}
void File::ensureAnimation() const {
if (!_animation) {
_animation.reset(new AnimationData(
animation(const_cast<File*>(this), &File::step_thumbOver),
animation(const_cast<File*>(this), &File::step_radial)));
}
}
void File::checkAnimationFinished() {
if (_animation && !_animation->_a_thumbOver.animating() && !_animation->radial.animating()) {
if (getShownDocument()->loaded()) {
_animation = nullptr;
}
}
}
bool File::updateStatusText() const {
bool showPause = false;
int32 statusSize = 0, realDuration = 0;
DocumentData *document = getShownDocument();
if (document->status == FileDownloadFailed || document->status == FileUploadFailed) {
statusSize = FileStatusSizeFailed;
} else if (document->status == FileUploading) {
statusSize = document->uploadOffset;
} else if (document->loading()) {
statusSize = document->loadOffset();
} else if (document->loaded()) {
if (document->voice()) {
statusSize = FileStatusSizeLoaded;
if (audioPlayer()) {
AudioMsgId playing;
auto playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Voice);
if (playing == AudioMsgId(document, FullMsgId()) && !(playbackState.state & AudioPlayerStoppedMask) && playbackState.state != AudioPlayerFinishing) {
statusSize = -1 - (playbackState.position / (playbackState.frequency ? playbackState.frequency : AudioVoiceMsgFrequency));
realDuration = playbackState.duration / (playbackState.frequency ? playbackState.frequency : AudioVoiceMsgFrequency);
showPause = (playbackState.state == AudioPlayerPlaying || playbackState.state == AudioPlayerResuming || playbackState.state == AudioPlayerStarting);
}
}
} else if (document->song()) {
statusSize = FileStatusSizeLoaded;
if (audioPlayer()) {
AudioMsgId playing;
auto playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Song);
if (playing == AudioMsgId(document, FullMsgId()) && !(playbackState.state & AudioPlayerStoppedMask) && playbackState.state != AudioPlayerFinishing) {
statusSize = -1 - (playbackState.position / (playbackState.frequency ? playbackState.frequency : AudioVoiceMsgFrequency));
realDuration = playbackState.duration / (playbackState.frequency ? playbackState.frequency : AudioVoiceMsgFrequency);
showPause = (playbackState.state == AudioPlayerPlaying || playbackState.state == AudioPlayerResuming || playbackState.state == AudioPlayerStarting);
}
if (!showPause && (playing == AudioMsgId(document, FullMsgId())) && App::main() && App::main()->player()->seekingSong(playing)) {
showPause = true;
}
}
} else {
statusSize = FileStatusSizeLoaded;
}
} else {
statusSize = FileStatusSizeReady;
}
if (statusSize != _statusSize) {
int32 duration = document->song() ? document->song()->duration : (document->voice() ? document->voice()->duration : -1);
setStatusSize(statusSize, document->size, duration, realDuration);
}
return showPause;
}
void File::setStatusSize(int32 newSize, int32 fullSize, int32 duration, qint64 realDuration) const {
_statusSize = newSize;
if (_statusSize == FileStatusSizeReady) {
_statusText = (duration >= 0) ? formatDurationAndSizeText(duration, fullSize) : (duration < -1 ? formatGifAndSizeText(fullSize) : formatSizeText(fullSize));
} else if (_statusSize == FileStatusSizeLoaded) {
_statusText = (duration >= 0) ? formatDurationText(duration) : (duration < -1 ? qsl("GIF") : formatSizeText(fullSize));
} else if (_statusSize == FileStatusSizeFailed) {
_statusText = lang(lng_attach_failed);
} else if (_statusSize >= 0) {
_statusText = formatDownloadText(_statusSize, fullSize);
} else {
_statusText = formatPlayedText(-_statusSize - 1, realDuration);
}
}
Contact::Contact(Result *result) : ItemBase(result)
, _title(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip)
, _description(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip) {
}
void Contact::initDimensions() {
_maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft;
int32 textWidth = _maxw - (st::inlineThumbSize + st::inlineThumbSkip);
TextParseOptions titleOpts = { 0, _maxw, st::semiboldFont->height, Qt::LayoutDirectionAuto };
_title.setText(st::semiboldFont, textOneLine(_result->getLayoutTitle()), titleOpts);
int32 titleHeight = qMin(_title.countHeight(_maxw), st::semiboldFont->height);
TextParseOptions descriptionOpts = { TextParseMultiline, _maxw, st::normalFont->height, Qt::LayoutDirectionAuto };
_description.setText(st::normalFont, _result->getLayoutDescription(), descriptionOpts);
int32 descriptionHeight = qMin(_description.countHeight(_maxw), st::normalFont->height);
_minh = st::msgFileSize;
_minh += st::inlineRowMargin * 2 + st::inlineRowBorder;
}
int32 Contact::resizeGetHeight(int32 width) {
_width = qMin(width, _maxw);
_height = _minh;
return _height;
}
void Contact::paint(Painter &p, const QRect &clip, const PaintContext *context) const {
int32 left = st::emojiPanHeaderLeft - st::inlineResultsLeft;
left = st::msgFileSize + st::inlineThumbSkip;
prepareThumb(st::msgFileSize, st::msgFileSize);
QRect rthumb(rtlrect(0, st::inlineRowMargin, st::msgFileSize, st::msgFileSize, _width));
p.drawPixmapLeft(rthumb.topLeft(), _width, _thumb);
int titleTop = st::inlineRowMargin + st::inlineRowFileNameTop;
int descriptionTop = st::inlineRowMargin + st::inlineRowFileDescriptionTop;
p.setPen(st::black);
_title.drawLeftElided(p, left, titleTop, _width - left, _width);
p.setPen(st::inlineDescriptionFg);
_description.drawLeftElided(p, left, descriptionTop, _width - left, _width);
if (!context->lastRow) {
p.fillRect(rtlrect(left, _height - st::inlineRowBorder, _width - left, st::inlineRowBorder, _width), st::inlineRowBorderFg);
}
}
void Contact::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const {
int left = (st::msgFileSize + st::inlineThumbSkip);
if (x >= 0 && x < left - st::inlineThumbSkip && y >= st::inlineRowMargin && y < st::inlineRowMargin + st::inlineThumbSize) {
return;
}
if (x >= left && x < _width && y >= 0 && y < _height) {
link = _send;
return;
}
}
void Contact::prepareThumb(int width, int height) const {
ImagePtr thumb = getResultThumb();
if (thumb->isNull()) {
if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) {
_thumb = getResultContactAvatar(width, height);
}
return;
}
if (thumb->loaded()) {
if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) {
int w = qMax(convertScale(thumb->width()), 1), h = qMax(convertScale(thumb->height()), 1);
if (w * height > h * width) {
if (height < h) {
w = w * height / h;
h = height;
}
} else {
if (width < w) {
h = h * width / w;
w = width;
}
}
_thumb = thumb->pixNoCache(w * cIntRetinaFactor(), h * cIntRetinaFactor(), ImagePixSmooth, width, height);
}
} else {
thumb->load();
}
}
Article::Article(Result *result, bool withThumb) : ItemBase(result)
, _url(getResultUrlHandler())
, _link(getResultContentUrlHandler())
, _withThumb(withThumb)
, _title(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip)
, _description(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip) {
LocationCoords location;
if (!_link && result->getLocationCoords(&location)) {
_link.reset(new LocationClickHandler(location));
}
_thumbLetter = getResultThumbLetter();
}
void Article::initDimensions() {
_maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft;
int32 textWidth = _maxw - (_withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : 0);
TextParseOptions titleOpts = { 0, _maxw, 2 * st::semiboldFont->height, Qt::LayoutDirectionAuto };
_title.setText(st::semiboldFont, textOneLine(_result->getLayoutTitle()), titleOpts);
int32 titleHeight = qMin(_title.countHeight(_maxw), 2 * st::semiboldFont->height);
int32 descriptionLines = (_withThumb || _url) ? 2 : 3;
QString description = _result->getLayoutDescription();
TextParseOptions descriptionOpts = { TextParseMultiline, _maxw, descriptionLines * st::normalFont->height, Qt::LayoutDirectionAuto };
_description.setText(st::normalFont, description, descriptionOpts);
int32 descriptionHeight = qMin(_description.countHeight(_maxw), descriptionLines * st::normalFont->height);
_minh = titleHeight + descriptionHeight;
if (_url) _minh += st::normalFont->height;
if (_withThumb) _minh = qMax(_minh, int32(st::inlineThumbSize));
_minh += st::inlineRowMargin * 2 + st::inlineRowBorder;
}
int32 Article::resizeGetHeight(int32 width) {
_width = qMin(width, _maxw);
if (_url) {
_urlText = getResultUrl();
_urlWidth = st::normalFont->width(_urlText);
if (_urlWidth > _width - st::inlineThumbSize - st::inlineThumbSkip) {
_urlText = st::normalFont->elided(_urlText, _width - st::inlineThumbSize - st::inlineThumbSkip);
_urlWidth = st::normalFont->width(_urlText);
}
}
_height = _minh;
return _height;
}
void Article::paint(Painter &p, const QRect &clip, const PaintContext *context) const {
int32 left = st::emojiPanHeaderLeft - st::inlineResultsLeft;
if (_withThumb) {
left = st::inlineThumbSize + st::inlineThumbSkip;
prepareThumb(st::inlineThumbSize, st::inlineThumbSize);
QRect rthumb(rtlrect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize, _width));
if (_thumb.isNull()) {
ImagePtr thumb = getResultThumb();
if (thumb->isNull() && !_thumbLetter.isEmpty()) {
int32 index = (_thumbLetter.at(0).unicode() % 4);
style::color colors[] = { st::msgFileRedColor, st::msgFileYellowColor, st::msgFileGreenColor, st::msgFileBlueColor };
p.fillRect(rthumb, colors[index]);
if (!_thumbLetter.isEmpty()) {
p.setFont(st::linksLetterFont);
p.setPen(st::white);
p.drawText(rthumb, _thumbLetter, style::al_center);
}
} else {
p.fillRect(rthumb, st::overviewPhotoBg);
}
} else {
p.drawPixmapLeft(rthumb.topLeft(), _width, _thumb);
}
}
p.setPen(st::black);
_title.drawLeftElided(p, left, st::inlineRowMargin, _width - left, _width, 2);
int32 titleHeight = qMin(_title.countHeight(_width - left), st::semiboldFont->height * 2);
p.setPen(st::inlineDescriptionFg);
int32 descriptionLines = (_withThumb || _url) ? 2 : 3;
_description.drawLeftElided(p, left, st::inlineRowMargin + titleHeight, _width - left, _width, descriptionLines);
if (_url) {
int32 descriptionHeight = qMin(_description.countHeight(_width - left), st::normalFont->height * descriptionLines);
p.drawTextLeft(left, st::inlineRowMargin + titleHeight + descriptionHeight, _width, _urlText, _urlWidth);
}
if (!context->lastRow) {
p.fillRect(rtlrect(left, _height - st::inlineRowBorder, _width - left, st::inlineRowBorder, _width), st::inlineRowBorderFg);
}
}
void Article::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const {
int left = _withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : 0;
if (x >= 0 && x < left - st::inlineThumbSkip && y >= st::inlineRowMargin && y < st::inlineRowMargin + st::inlineThumbSize) {
link = _link;
return;
}
if (x >= left && x < _width && y >= 0 && y < _height) {
if (_url) {
int32 left = st::inlineThumbSize + st::inlineThumbSkip;
int32 titleHeight = qMin(_title.countHeight(_width - left), st::semiboldFont->height * 2);
int32 descriptionLines = 2;
int32 descriptionHeight = qMin(_description.countHeight(_width - left), st::normalFont->height * descriptionLines);
if (rtlrect(left, st::inlineRowMargin + titleHeight + descriptionHeight, _urlWidth, st::normalFont->height, _width).contains(x, y)) {
link = _url;
return;
}
}
link = _send;
return;
}
}
void Article::prepareThumb(int width, int height) const {
ImagePtr thumb = getResultThumb();
if (thumb->isNull()) {
if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) {
_thumb = getResultContactAvatar(width, height);
}
return;
}
if (thumb->loaded()) {
if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) {
int w = qMax(convertScale(thumb->width()), 1), h = qMax(convertScale(thumb->height()), 1);
if (w * height > h * width) {
if (height < h) {
w = w * height / h;
h = height;
}
} else {
if (width < w) {
h = h * width / w;
w = width;
}
}
_thumb = thumb->pixNoCache(w * cIntRetinaFactor(), h * cIntRetinaFactor(), ImagePixSmooth, width, height);
}
} else {
thumb->load();
}
}
Game::Game(Result *result) : ItemBase(result)
, _title(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip)
, _description(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip) {
countFrameSize();
}
void Game::countFrameSize() {
if (auto document = getResultDocument()) {
if (document->isAnimation()) {
auto documentSize = document->dimensions;
if (documentSize.isEmpty()) {
documentSize = QSize(st::inlineThumbSize, st::inlineThumbSize);
}
auto resizeByHeight1 = (documentSize.width() > documentSize.height()) && (documentSize.height() >= st::inlineThumbSize);
auto resizeByHeight2 = (documentSize.height() >= documentSize.width()) && (documentSize.width() < st::inlineThumbSize);
if (resizeByHeight1 || resizeByHeight2) {
if (documentSize.height() > st::inlineThumbSize) {
_frameSize = QSize((documentSize.width() * st::inlineThumbSize) / documentSize.height(), st::inlineThumbSize);
}
} else {
if (documentSize.width() > st::inlineThumbSize) {
_frameSize = QSize(st::inlineThumbSize, (documentSize.height() * st::inlineThumbSize) / documentSize.width());
}
}
if (!_frameSize.width()) {
_frameSize.setWidth(1);
}
if (!_frameSize.height()) {
_frameSize.setHeight(1);
}
}
}
}
void Game::initDimensions() {
_maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft;
int32 textWidth = _maxw - (st::inlineThumbSize + st::inlineThumbSkip);
TextParseOptions titleOpts = { 0, _maxw, 2 * st::semiboldFont->height, Qt::LayoutDirectionAuto };
_title.setText(st::semiboldFont, textOneLine(_result->getLayoutTitle()), titleOpts);
int32 titleHeight = qMin(_title.countHeight(_maxw), 2 * st::semiboldFont->height);
int32 descriptionLines = 2;
QString description = _result->getLayoutDescription();
TextParseOptions descriptionOpts = { TextParseMultiline, _maxw, descriptionLines * st::normalFont->height, Qt::LayoutDirectionAuto };
_description.setText(st::normalFont, description, descriptionOpts);
int32 descriptionHeight = qMin(_description.countHeight(_maxw), descriptionLines * st::normalFont->height);
_minh = titleHeight + descriptionHeight;
accumulate_max(_minh, st::inlineThumbSize);
_minh += st::inlineRowMargin * 2 + st::inlineRowBorder;
}
void Game::setPosition(int32 position) {
ItemBase::setPosition(position);
if (_position < 0) {
_gif.reset();
}
}
void Game::paint(Painter &p, const QRect &clip, const PaintContext *context) const {
int32 left = st::emojiPanHeaderLeft - st::inlineResultsLeft;
left = st::inlineThumbSize + st::inlineThumbSkip;
auto rthumb = rtlrect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize, _width);
// Gif thumb
auto thumbDisplayed = false, radial = false;
auto document = getResultDocument();
auto animatedThumb = document && document->isAnimation();
if (animatedThumb) {
document->automaticLoad(nullptr);
bool loaded = document->loaded(), loading = document->loading(), displayLoading = document->displayLoading();
if (loaded && !_gif && !_gif.isBad()) {
auto that = const_cast<Game*>(this);
that->_gif = Media::Clip::MakeReader(document->location(), document->data(), [that](Media::Clip::Notification notification) {
that->clipCallback(notification);
});
if (_gif) _gif->setAutoplay();
}
bool animating = (_gif && _gif->started());
if (displayLoading) {
if (!_radial) {
_radial = std_::make_unique<Ui::RadialAnimation>(animation(const_cast<Game*>(this), &Game::step_radial));
}
if (!_radial->animating()) {
_radial->start(document->progress());
}
}
radial = isRadialAnimation(context->ms);
if (animating) {
if (!_thumb.isNull()) _thumb = QPixmap();
auto animationThumb = _gif->current(_frameSize.width(), _frameSize.height(), st::inlineThumbSize, st::inlineThumbSize, context->paused ? 0 : context->ms);
p.drawPixmapLeft(rthumb.topLeft(), _width, animationThumb);
thumbDisplayed = true;
}
}
if (!thumbDisplayed) {
prepareThumb(st::inlineThumbSize, st::inlineThumbSize);
if (_thumb.isNull()) {
p.fillRect(rthumb, st::overviewPhotoBg);
} else {
p.drawPixmapLeft(rthumb.topLeft(), _width, _thumb);
}
}
if (radial) {
p.fillRect(rthumb, st::msgDateImgBg);
QRect inner((st::inlineThumbSize - st::msgFileSize) / 2, (st::inlineThumbSize - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize);
if (radial) {
p.setOpacity(1);
QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
_radial->draw(p, rinner, st::msgFileRadialLine, st::msgInBg);
}
}
p.setPen(st::black);
_title.drawLeftElided(p, left, st::inlineRowMargin, _width - left, _width, 2);
int32 titleHeight = qMin(_title.countHeight(_width - left), st::semiboldFont->height * 2);
p.setPen(st::inlineDescriptionFg);
int32 descriptionLines = 2;
_description.drawLeftElided(p, left, st::inlineRowMargin + titleHeight, _width - left, _width, descriptionLines);
if (!context->lastRow) {
p.fillRect(rtlrect(left, _height - st::inlineRowBorder, _width - left, st::inlineRowBorder, _width), st::inlineRowBorderFg);
}
}
void Game::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const {
int left = st::inlineThumbSize + st::inlineThumbSkip;
if (x >= 0 && x < left - st::inlineThumbSkip && y >= st::inlineRowMargin && y < st::inlineRowMargin + st::inlineThumbSize) {
link = _send;
return;
}
if (x >= left && x < _width && y >= 0 && y < _height) {
link = _send;
return;
}
}
void Game::prepareThumb(int width, int height) const {
auto thumb = ([this]() {
if (auto photo = getResultPhoto()) {
return photo->medium;
} else if (auto document = getResultDocument()) {
return document->thumb;
}
return ImagePtr();
})();
if (thumb->isNull()) {
return;
}
if (thumb->loaded()) {
if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) {
int w = qMax(convertScale(thumb->width()), 1), h = qMax(convertScale(thumb->height()), 1);
auto resizeByHeight1 = (w * height > h * width) && (h >= height);
auto resizeByHeight2 = (h * width >= w * height) && (w < width);
if (resizeByHeight1 || resizeByHeight2) {
if (h > height) {
w = w * height / h;
h = height;
}
} else {
if (w > width) {
h = h * width / w;
w = width;
}
}
_thumb = thumb->pixNoCache(w * cIntRetinaFactor(), h * cIntRetinaFactor(), ImagePixSmooth, width, height);
}
} else {
thumb->load();
}
}
bool Game::isRadialAnimation(uint64 ms) const {
if (!_radial || !_radial->animating()) return false;
_radial->step(ms);
return _radial && _radial->animating();
}
void Game::step_radial(uint64 ms, bool timer) {
if (timer) {
update();
} else {
auto document = getResultDocument();
_radial->update(document->progress(), !document->loading() || document->loaded(), ms);
if (!_radial->animating() && document->loaded()) {
_radial.reset();
}
}
}
void Game::clipCallback(Media::Clip::Notification notification) {
using namespace Media::Clip;
switch (notification) {
case NotificationReinit: {
if (_gif) {
if (_gif->state() == State::Error) {
_gif.setBad();
getResultDocument()->forget();
} else if (_gif->ready() && !_gif->started()) {
_gif->start(_frameSize.width(), _frameSize.height(), st::inlineThumbSize, st::inlineThumbSize, ImageRoundRadius::None);
} else if (_gif->autoPausedGif() && !Ui::isInlineItemVisible(this)) {
_gif.reset();
getResultDocument()->forget();
}
}
update();
} break;
case NotificationRepaint: {
if (_gif && !_gif->currentDisplayed()) {
update();
}
} break;
}
}
} // namespace internal
} // namespace Layout
} // namespace InlineBots