2018-12-18 10:45:06 +00:00
|
|
|
/*
|
|
|
|
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 "history/media/history_media_video.h"
|
|
|
|
|
|
|
|
#include "history/media/history_media_common.h"
|
|
|
|
#include "layout.h"
|
|
|
|
#include "history/history_item_components.h"
|
|
|
|
#include "history/history_item.h"
|
2019-01-03 12:36:01 +00:00
|
|
|
#include "history/history.h"
|
2018-12-18 10:45:06 +00:00
|
|
|
#include "history/view/history_view_element.h"
|
|
|
|
#include "history/view/history_view_cursor_state.h"
|
|
|
|
#include "ui/image/image.h"
|
|
|
|
#include "ui/grouped_layout.h"
|
|
|
|
#include "data/data_session.h"
|
|
|
|
#include "data/data_document.h"
|
2019-01-04 11:09:48 +00:00
|
|
|
#include "data/data_file_origin.h"
|
2018-12-18 10:45:06 +00:00
|
|
|
#include "styles/style_history.h"
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
using TextState = HistoryView::TextState;
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
HistoryVideo::HistoryVideo(
|
|
|
|
not_null<Element*> parent,
|
|
|
|
not_null<HistoryItem*> realParent,
|
|
|
|
not_null<DocumentData*> document)
|
|
|
|
: HistoryFileMedia(parent, realParent)
|
|
|
|
, _data(document)
|
|
|
|
, _thumbw(1)
|
|
|
|
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
|
|
|
|
_caption = createCaption(realParent);
|
|
|
|
|
|
|
|
setDocumentLinks(_data, realParent);
|
|
|
|
|
|
|
|
setStatusSize(FileStatusSizeReady);
|
|
|
|
|
2019-01-25 14:37:28 +00:00
|
|
|
_data->loadThumbnail(realParent->fullId());
|
|
|
|
}
|
|
|
|
|
|
|
|
QSize HistoryVideo::sizeForAspectRatio() const {
|
|
|
|
// We use size only for aspect ratio and we want to have it
|
|
|
|
// as close to the thumbnail as possible.
|
|
|
|
//if (!_data->dimensions.isEmpty()) {
|
|
|
|
// return _data->dimensions;
|
|
|
|
//}
|
|
|
|
if (const auto thumb = _data->thumbnail()) {
|
|
|
|
if (!thumb->size().isEmpty()) {
|
|
|
|
return thumb->size();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return { 1, 1 };
|
2018-12-18 10:45:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QSize HistoryVideo::countOptimalSize() {
|
|
|
|
if (_parent->media() != this) {
|
|
|
|
_caption = Text();
|
|
|
|
} else if (_caption.hasSkipBlock()) {
|
|
|
|
_caption.updateSkipBlock(
|
|
|
|
_parent->skipBlockWidth(),
|
|
|
|
_parent->skipBlockHeight());
|
|
|
|
}
|
|
|
|
|
2019-01-25 14:37:28 +00:00
|
|
|
const auto size = sizeForAspectRatio();
|
|
|
|
auto tw = ConvertScale(size.width());
|
|
|
|
auto th = ConvertScale(size.height());
|
2018-12-18 10:45:06 +00:00
|
|
|
if (!tw || !th) {
|
|
|
|
tw = th = 1;
|
|
|
|
}
|
|
|
|
if (tw * st::msgVideoSize.height() > th * st::msgVideoSize.width()) {
|
|
|
|
th = qRound((st::msgVideoSize.width() / float64(tw)) * th);
|
|
|
|
tw = st::msgVideoSize.width();
|
|
|
|
} else {
|
|
|
|
tw = qRound((st::msgVideoSize.height() / float64(th)) * tw);
|
|
|
|
th = st::msgVideoSize.height();
|
|
|
|
}
|
|
|
|
|
|
|
|
_thumbw = qMax(tw, 1);
|
|
|
|
_thumbh = qMax(th, 1);
|
|
|
|
|
|
|
|
auto minWidth = qMax(st::minPhotoSize, _parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
|
|
|
|
minWidth = qMax(minWidth, documentMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
|
|
|
|
auto maxWidth = qMax(_thumbw, minWidth);
|
|
|
|
auto minHeight = qMax(th, st::minPhotoSize);
|
|
|
|
if (_parent->hasBubble() && !_caption.isEmpty()) {
|
|
|
|
const auto captionw = maxWidth
|
|
|
|
- st::msgPadding.left()
|
|
|
|
- st::msgPadding.right();
|
|
|
|
minHeight += st::mediaCaptionSkip + _caption.countHeight(captionw);
|
|
|
|
if (isBubbleBottom()) {
|
|
|
|
minHeight += st::msgPadding.bottom();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return { maxWidth, minHeight };
|
|
|
|
}
|
|
|
|
|
|
|
|
QSize HistoryVideo::countCurrentSize(int newWidth) {
|
2019-01-25 14:37:28 +00:00
|
|
|
const auto size = sizeForAspectRatio();
|
|
|
|
auto tw = ConvertScale(size.width());
|
|
|
|
auto th = ConvertScale(size.height());
|
2018-12-18 10:45:06 +00:00
|
|
|
if (!tw || !th) {
|
|
|
|
tw = th = 1;
|
|
|
|
}
|
|
|
|
if (tw * st::msgVideoSize.height() > th * st::msgVideoSize.width()) {
|
|
|
|
th = qRound((st::msgVideoSize.width() / float64(tw)) * th);
|
|
|
|
tw = st::msgVideoSize.width();
|
|
|
|
} else {
|
|
|
|
tw = qRound((st::msgVideoSize.height() / float64(th)) * tw);
|
|
|
|
th = st::msgVideoSize.height();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newWidth < tw) {
|
|
|
|
th = qRound((newWidth / float64(tw)) * th);
|
|
|
|
tw = newWidth;
|
|
|
|
}
|
|
|
|
|
|
|
|
_thumbw = qMax(tw, 1);
|
|
|
|
_thumbh = qMax(th, 1);
|
|
|
|
auto minWidth = qMax(st::minPhotoSize, _parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
|
|
|
|
minWidth = qMax(minWidth, documentMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
|
|
|
|
newWidth = qMax(_thumbw, minWidth);
|
|
|
|
auto newHeight = qMax(th, st::minPhotoSize);
|
|
|
|
if (_parent->hasBubble() && !_caption.isEmpty()) {
|
|
|
|
const auto captionw = newWidth
|
|
|
|
- st::msgPadding.left()
|
|
|
|
- st::msgPadding.right();
|
|
|
|
newHeight += st::mediaCaptionSkip + _caption.countHeight(captionw);
|
|
|
|
if (isBubbleBottom()) {
|
|
|
|
newHeight += st::msgPadding.bottom();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return { newWidth, newHeight };
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryVideo::draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const {
|
|
|
|
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return;
|
|
|
|
|
|
|
|
_data->automaticLoad(_realParent->fullId(), _parent->data());
|
|
|
|
bool loaded = _data->loaded(), displayLoading = _data->displayLoading();
|
|
|
|
bool selected = (selection == FullSelection);
|
|
|
|
|
|
|
|
auto paintx = 0, painty = 0, paintw = width(), painth = height();
|
|
|
|
bool bubble = _parent->hasBubble();
|
|
|
|
|
|
|
|
int captionw = paintw - st::msgPadding.left() - st::msgPadding.right();
|
|
|
|
|
|
|
|
if (displayLoading) {
|
|
|
|
ensureAnimation();
|
|
|
|
if (!_animation->radial.animating()) {
|
|
|
|
_animation->radial.start(_data->progress());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
updateStatusText();
|
|
|
|
bool radial = isRadialAnimation(ms);
|
|
|
|
|
|
|
|
if (bubble) {
|
|
|
|
if (!_caption.isEmpty()) {
|
|
|
|
painth -= st::mediaCaptionSkip + _caption.countHeight(captionw);
|
|
|
|
if (isBubbleBottom()) {
|
|
|
|
painth -= st::msgPadding.bottom();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
App::roundShadow(p, 0, 0, paintw, painth, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto inWebPage = (_parent->media() != this);
|
|
|
|
auto roundRadius = inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large;
|
|
|
|
auto roundCorners = inWebPage ? RectPart::AllCorners : ((isBubbleTop() ? (RectPart::TopLeft | RectPart::TopRight) : RectPart::None)
|
|
|
|
| ((isBubbleBottom() && _caption.isEmpty()) ? (RectPart::BottomLeft | RectPart::BottomRight) : RectPart::None));
|
|
|
|
QRect rthumb(rtlrect(paintx, painty, paintw, painth, width()));
|
|
|
|
|
|
|
|
const auto good = _data->goodThumbnail();
|
|
|
|
if (good && good->loaded()) {
|
|
|
|
p.drawPixmap(rthumb.topLeft(), good->pixSingle({}, _thumbw, _thumbh, paintw, painth, roundRadius, roundCorners));
|
|
|
|
} else {
|
|
|
|
if (good) {
|
|
|
|
good->load({});
|
|
|
|
}
|
2019-02-17 08:31:04 +00:00
|
|
|
const auto normal = _data->thumbnail();
|
|
|
|
if (normal && normal->loaded()) {
|
|
|
|
p.drawPixmap(rthumb.topLeft(), normal->pixSingle(_realParent->fullId(), _thumbw, _thumbh, paintw, painth, roundRadius, roundCorners));
|
|
|
|
} else if (const auto blurred = _data->thumbnailInline()) {
|
|
|
|
p.drawPixmap(rthumb.topLeft(), blurred->pixBlurredSingle(_realParent->fullId(), _thumbw, _thumbh, paintw, painth, roundRadius, roundCorners));
|
|
|
|
} else {
|
|
|
|
const auto roundTop = (roundCorners & RectPart::TopLeft);
|
|
|
|
const auto roundBottom = (roundCorners & RectPart::BottomLeft);
|
|
|
|
const auto margin = inWebPage
|
|
|
|
? st::buttonRadius
|
|
|
|
: st::historyMessageRadius;
|
|
|
|
const auto parts = roundCorners
|
|
|
|
| RectPart::NoTopBottom
|
|
|
|
| (roundTop ? RectPart::Top : RectPart::None)
|
|
|
|
| (roundBottom ? RectPart::Bottom : RectPart::None);
|
|
|
|
App::roundRect(p, rthumb.marginsAdded({ 0, roundTop ? 0 : margin, 0, roundBottom ? 0 : margin }), st::imageBg, roundRadius, parts);
|
2019-01-25 14:37:28 +00:00
|
|
|
}
|
2018-12-18 10:45:06 +00:00
|
|
|
}
|
|
|
|
if (selected) {
|
|
|
|
App::complexOverlayRect(p, rthumb, roundRadius, roundCorners);
|
|
|
|
}
|
|
|
|
|
|
|
|
QRect inner(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize);
|
|
|
|
p.setPen(Qt::NoPen);
|
|
|
|
if (selected) {
|
|
|
|
p.setBrush(st::msgDateImgBgSelected);
|
|
|
|
} else if (isThumbAnimation(ms)) {
|
|
|
|
auto over = _animation->a_thumbOver.current();
|
|
|
|
p.setBrush(anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, over));
|
|
|
|
} else {
|
|
|
|
bool over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel);
|
|
|
|
p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
PainterHighQualityEnabler hq(p);
|
|
|
|
p.drawEllipse(inner);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!selected && _animation) {
|
|
|
|
p.setOpacity(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto icon = ([this, radial, selected, loaded]() -> const style::icon * {
|
|
|
|
if (loaded && !radial) {
|
|
|
|
return &(selected ? st::historyFileThumbPlaySelected : st::historyFileThumbPlay);
|
|
|
|
} else if (radial || _data->loading()) {
|
|
|
|
if (_parent->data()->id > 0 || _data->uploading()) {
|
|
|
|
return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel);
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return &(selected ? st::historyFileThumbDownloadSelected : st::historyFileThumbDownload);
|
|
|
|
})();
|
|
|
|
if (icon) {
|
|
|
|
icon->paintInCenter(p, inner);
|
|
|
|
}
|
|
|
|
if (radial) {
|
|
|
|
QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
|
|
|
|
_animation->radial.draw(p, rinner, st::msgFileRadialLine, selected ? st::historyFileThumbRadialFgSelected : st::historyFileThumbRadialFg);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto statusX = paintx + st::msgDateImgDelta + st::msgDateImgPadding.x(), statusY = painty + st::msgDateImgDelta + st::msgDateImgPadding.y();
|
|
|
|
auto statusW = st::normalFont->width(_statusText) + 2 * st::msgDateImgPadding.x();
|
|
|
|
auto statusH = st::normalFont->height + 2 * st::msgDateImgPadding.y();
|
|
|
|
App::roundRect(p, rtlrect(statusX - st::msgDateImgPadding.x(), statusY - st::msgDateImgPadding.y(), statusW, statusH, width()), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners);
|
|
|
|
p.setFont(st::normalFont);
|
|
|
|
p.setPen(st::msgDateImgFg);
|
|
|
|
p.drawTextLeft(statusX, statusY, width(), _statusText, statusW - 2 * st::msgDateImgPadding.x());
|
|
|
|
|
|
|
|
// date
|
|
|
|
if (!_caption.isEmpty()) {
|
|
|
|
auto outbg = _parent->hasOutLayout();
|
|
|
|
p.setPen(outbg ? (selected ? st::historyTextOutFgSelected : st::historyTextOutFg) : (selected ? st::historyTextInFgSelected : st::historyTextInFg));
|
|
|
|
_caption.draw(p, st::msgPadding.left(), painty + painth + st::mediaCaptionSkip, captionw, style::al_left, 0, -1, selection);
|
|
|
|
} else if (_parent->media() == this) {
|
|
|
|
auto fullRight = paintx + paintw, fullBottom = painty + painth;
|
|
|
|
_parent->drawInfo(p, fullRight, fullBottom, 2 * paintx + paintw, selected, InfoDisplayType::Image);
|
|
|
|
if (!bubble && _parent->displayRightAction()) {
|
|
|
|
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
|
|
|
|
auto fastShareTop = (fullBottom - st::historyFastShareBottom - st::historyFastShareSize);
|
|
|
|
_parent->drawRightAction(p, fastShareLeft, fastShareTop, 2 * paintx + paintw);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TextState HistoryVideo::textState(QPoint point, StateRequest request) const {
|
|
|
|
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
auto result = TextState(_parent);
|
|
|
|
bool loaded = _data->loaded();
|
|
|
|
|
|
|
|
auto paintx = 0, painty = 0, paintw = width(), painth = height();
|
|
|
|
bool bubble = _parent->hasBubble();
|
|
|
|
|
|
|
|
if (bubble && !_caption.isEmpty()) {
|
|
|
|
const auto captionw = paintw
|
|
|
|
- st::msgPadding.left()
|
|
|
|
- st::msgPadding.right();
|
|
|
|
painth -= _caption.countHeight(captionw);
|
|
|
|
if (isBubbleBottom()) {
|
|
|
|
painth -= st::msgPadding.bottom();
|
|
|
|
}
|
|
|
|
if (QRect(st::msgPadding.left(), painth, captionw, height() - painth).contains(point)) {
|
|
|
|
result = TextState(_parent, _caption.getState(
|
|
|
|
point - QPoint(st::msgPadding.left(), painth),
|
|
|
|
captionw,
|
|
|
|
request.forText()));
|
|
|
|
}
|
|
|
|
painth -= st::mediaCaptionSkip;
|
|
|
|
}
|
|
|
|
if (QRect(paintx, painty, paintw, painth).contains(point)) {
|
|
|
|
if (_data->uploading()) {
|
|
|
|
result.link = _cancell;
|
|
|
|
} else {
|
|
|
|
result.link = loaded ? _openl : (_data->loading() ? _cancell : _savel);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (_caption.isEmpty() && _parent->media() == this) {
|
|
|
|
auto fullRight = paintx + paintw;
|
|
|
|
auto fullBottom = painty + painth;
|
|
|
|
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayType::Image)) {
|
|
|
|
result.cursor = CursorState::Date;
|
|
|
|
}
|
|
|
|
if (!bubble && _parent->displayRightAction()) {
|
|
|
|
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
|
|
|
|
auto fastShareTop = (fullBottom - st::historyFastShareBottom - st::historyFastShareSize);
|
|
|
|
if (QRect(fastShareLeft, fastShareTop, st::historyFastShareSize, st::historyFastShareSize).contains(point)) {
|
|
|
|
result.link = _parent->rightActionLink();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
QSize HistoryVideo::sizeForGrouping() const {
|
2019-01-25 14:37:28 +00:00
|
|
|
return sizeForAspectRatio();
|
2018-12-18 10:45:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryVideo::drawGrouped(
|
|
|
|
Painter &p,
|
|
|
|
const QRect &clip,
|
|
|
|
TextSelection selection,
|
|
|
|
TimeMs ms,
|
|
|
|
const QRect &geometry,
|
|
|
|
RectParts corners,
|
|
|
|
not_null<uint64*> cacheKey,
|
|
|
|
not_null<QPixmap*> cache) const {
|
|
|
|
_data->automaticLoad(_realParent->fullId(), _parent->data());
|
|
|
|
|
|
|
|
validateGroupedCache(geometry, corners, cacheKey, cache);
|
|
|
|
|
|
|
|
const auto selected = (selection == FullSelection);
|
|
|
|
const auto loaded = _data->loaded();
|
|
|
|
const auto displayLoading = _data->displayLoading();
|
|
|
|
const auto bubble = _parent->hasBubble();
|
|
|
|
|
|
|
|
if (displayLoading) {
|
|
|
|
ensureAnimation();
|
|
|
|
if (!_animation->radial.animating()) {
|
|
|
|
_animation->radial.start(_data->progress());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const auto radial = isRadialAnimation(ms);
|
|
|
|
|
|
|
|
if (!bubble) {
|
|
|
|
// App::roundShadow(p, 0, 0, paintw, painth, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
|
|
|
|
}
|
|
|
|
p.drawPixmap(geometry.topLeft(), *cache);
|
|
|
|
if (selected) {
|
|
|
|
const auto roundRadius = ImageRoundRadius::Large;
|
|
|
|
App::complexOverlayRect(p, geometry, roundRadius, corners);
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto radialOpacity = radial
|
|
|
|
? _animation->radial.opacity()
|
|
|
|
: 1.;
|
|
|
|
const auto backOpacity = (loaded && !_data->uploading())
|
|
|
|
? radialOpacity
|
|
|
|
: 1.;
|
|
|
|
const auto radialSize = st::historyGroupRadialSize;
|
|
|
|
const auto inner = QRect(
|
|
|
|
geometry.x() + (geometry.width() - radialSize) / 2,
|
|
|
|
geometry.y() + (geometry.height() - radialSize) / 2,
|
|
|
|
radialSize,
|
|
|
|
radialSize);
|
|
|
|
p.setPen(Qt::NoPen);
|
|
|
|
if (selected) {
|
|
|
|
p.setBrush(st::msgDateImgBgSelected);
|
|
|
|
} else if (isThumbAnimation(ms)) {
|
|
|
|
auto over = _animation->a_thumbOver.current();
|
|
|
|
p.setBrush(anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, over));
|
|
|
|
} else {
|
|
|
|
auto over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel);
|
|
|
|
p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg);
|
|
|
|
}
|
|
|
|
|
|
|
|
p.setOpacity(backOpacity * p.opacity());
|
|
|
|
|
|
|
|
{
|
|
|
|
PainterHighQualityEnabler hq(p);
|
|
|
|
p.drawEllipse(inner);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto icon = [&]() -> const style::icon * {
|
|
|
|
if (_data->waitingForAlbum()) {
|
|
|
|
return &(selected ? st::historyFileThumbWaitingSelected : st::historyFileThumbWaiting);
|
|
|
|
} else if (loaded && !radial) {
|
|
|
|
return &(selected ? st::historyFileThumbPlaySelected : st::historyFileThumbPlay);
|
|
|
|
} else if (radial || _data->loading()) {
|
|
|
|
if (_parent->data()->id > 0 || _data->uploading()) {
|
|
|
|
return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel);
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return &(selected ? st::historyFileThumbDownloadSelected : st::historyFileThumbDownload);
|
|
|
|
}();
|
|
|
|
const auto previous = [&]() -> const style::icon* {
|
|
|
|
if (_data->waitingForAlbum()) {
|
|
|
|
return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel);
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}();
|
|
|
|
p.setOpacity(backOpacity);
|
|
|
|
if (icon) {
|
|
|
|
if (previous && radialOpacity > 0. && radialOpacity < 1.) {
|
|
|
|
PaintInterpolatedIcon(p, *icon, *previous, radialOpacity, inner);
|
|
|
|
} else {
|
|
|
|
icon->paintInCenter(p, inner);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
p.setOpacity(1);
|
|
|
|
if (radial) {
|
|
|
|
const auto line = st::historyGroupRadialLine;
|
|
|
|
const auto rinner = inner.marginsRemoved({ line, line, line, line });
|
|
|
|
const auto color = selected
|
|
|
|
? st::historyFileThumbRadialFgSelected
|
|
|
|
: st::historyFileThumbRadialFg;
|
|
|
|
_animation->radial.draw(p, rinner, line, color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TextState HistoryVideo::getStateGrouped(
|
|
|
|
const QRect &geometry,
|
|
|
|
QPoint point,
|
|
|
|
StateRequest request) const {
|
|
|
|
if (!geometry.contains(point)) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
return TextState(_parent, _data->uploading()
|
|
|
|
? _cancell
|
|
|
|
: _data->loaded()
|
|
|
|
? _openl
|
|
|
|
: _data->loading()
|
|
|
|
? _cancell
|
|
|
|
: _savel);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HistoryVideo::uploading() const {
|
|
|
|
return _data->uploading();
|
|
|
|
}
|
|
|
|
|
|
|
|
float64 HistoryVideo::dataProgress() const {
|
|
|
|
return _data->progress();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HistoryVideo::dataFinished() const {
|
|
|
|
return !_data->loading()
|
|
|
|
&& (!_data->uploading() || _data->waitingForAlbum());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HistoryVideo::dataLoaded() const {
|
|
|
|
return _data->loaded();
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryVideo::validateGroupedCache(
|
|
|
|
const QRect &geometry,
|
|
|
|
RectParts corners,
|
|
|
|
not_null<uint64*> cacheKey,
|
|
|
|
not_null<QPixmap*> cache) const {
|
|
|
|
using Option = Images::Option;
|
|
|
|
const auto good = _data->goodThumbnail();
|
|
|
|
const auto useGood = (good && good->loaded());
|
2019-01-25 14:37:28 +00:00
|
|
|
const auto thumb = _data->thumbnail();
|
|
|
|
const auto useThumb = (thumb && thumb->loaded());
|
|
|
|
const auto image = useGood
|
|
|
|
? good
|
|
|
|
: useThumb
|
|
|
|
? thumb
|
|
|
|
: _data->thumbnailInline();
|
2018-12-18 10:45:06 +00:00
|
|
|
if (good && !useGood) {
|
|
|
|
good->load({});
|
|
|
|
}
|
|
|
|
|
2019-01-25 14:37:28 +00:00
|
|
|
const auto loadLevel = useGood ? 3 : useThumb ? 2 : image ? 1 : 0;
|
2018-12-18 10:45:06 +00:00
|
|
|
const auto width = geometry.width();
|
|
|
|
const auto height = geometry.height();
|
|
|
|
const auto options = Option::Smooth
|
|
|
|
| Option::RoundedLarge
|
|
|
|
| (useGood ? Option(0) : Option::Blurred)
|
|
|
|
| ((corners & RectPart::TopLeft) ? Option::RoundedTopLeft : Option::None)
|
|
|
|
| ((corners & RectPart::TopRight) ? Option::RoundedTopRight : Option::None)
|
|
|
|
| ((corners & RectPart::BottomLeft) ? Option::RoundedBottomLeft : Option::None)
|
|
|
|
| ((corners & RectPart::BottomRight) ? Option::RoundedBottomRight : Option::None);
|
|
|
|
const auto key = (uint64(width) << 48)
|
|
|
|
| (uint64(height) << 32)
|
|
|
|
| (uint64(options) << 16)
|
|
|
|
| (uint64(loadLevel));
|
|
|
|
if (*cacheKey == key) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-01-25 14:37:28 +00:00
|
|
|
const auto original = sizeForAspectRatio();
|
|
|
|
const auto originalWidth = ConvertScale(original.width());
|
|
|
|
const auto originalHeight = ConvertScale(original.height());
|
2018-12-18 10:45:06 +00:00
|
|
|
const auto pixSize = Ui::GetImageScaleSizeForGeometry(
|
|
|
|
{ originalWidth, originalHeight },
|
|
|
|
{ width, height });
|
|
|
|
const auto pixWidth = pixSize.width() * cIntRetinaFactor();
|
|
|
|
const auto pixHeight = pixSize.height() * cIntRetinaFactor();
|
|
|
|
|
|
|
|
*cacheKey = key;
|
2019-02-17 08:31:04 +00:00
|
|
|
*cache = (image ? image : Image::BlankMedia().get())->pixNoCache(
|
|
|
|
_realParent->fullId(),
|
|
|
|
pixWidth,
|
|
|
|
pixHeight,
|
|
|
|
options,
|
|
|
|
width,
|
|
|
|
height);
|
2018-12-18 10:45:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryVideo::setStatusSize(int newSize) const {
|
|
|
|
HistoryFileMedia::setStatusSize(newSize, _data->size, _data->duration(), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
TextWithEntities HistoryVideo::selectedText(TextSelection selection) const {
|
|
|
|
return _caption.originalTextWithEntities(selection, ExpandLinksAll);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HistoryVideo::needsBubble() const {
|
|
|
|
if (!_caption.isEmpty()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
const auto item = _parent->data();
|
|
|
|
return item->viaBot()
|
|
|
|
|| item->Has<HistoryMessageReply>()
|
|
|
|
|| _parent->displayForwardedFrom()
|
|
|
|
|| _parent->displayFromName();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryVideo::parentTextUpdated() {
|
|
|
|
_caption = (_parent->media() == this)
|
|
|
|
? createCaption(_parent->data())
|
|
|
|
: Text();
|
2019-01-03 12:36:01 +00:00
|
|
|
history()->owner().requestViewResize(_parent);
|
2018-12-18 10:45:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryVideo::updateStatusText() const {
|
|
|
|
auto showPause = false;
|
|
|
|
auto statusSize = 0;
|
|
|
|
auto realDuration = 0;
|
|
|
|
if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) {
|
|
|
|
statusSize = FileStatusSizeFailed;
|
|
|
|
} else if (_data->uploading()) {
|
|
|
|
statusSize = _data->uploadingData->offset;
|
|
|
|
} else if (_data->loading()) {
|
|
|
|
statusSize = _data->loadOffset();
|
|
|
|
} else if (_data->loaded()) {
|
|
|
|
statusSize = FileStatusSizeLoaded;
|
|
|
|
} else {
|
|
|
|
statusSize = FileStatusSizeReady;
|
|
|
|
}
|
|
|
|
if (statusSize != _statusSize) {
|
|
|
|
setStatusSize(statusSize);
|
|
|
|
}
|
|
|
|
}
|