From 6100c1dccac6124007422905411394db08e4ba71 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 13 Dec 2015 18:21:20 +0300 Subject: [PATCH] video redesigned --- Telegram/Resources/style.txt | 4 +- Telegram/SourceFiles/gui/images.h | 2 +- Telegram/SourceFiles/history.cpp | 610 ++++++++++++++++++------------ Telegram/SourceFiles/history.h | 120 +++--- Telegram/SourceFiles/structs.cpp | 12 + Telegram/SourceFiles/structs.h | 10 + 6 files changed, 453 insertions(+), 305 deletions(-) diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index bbec08d02..9c234ea0b 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -1240,6 +1240,8 @@ msgFileInPlaySelected: sprite(180px, 164px, 20px, 18px); msgFileOverDuration: 200; msgFileRadialLine: 4px; +msgVideoSize: size(320px, 240px); + sendPadding: 9px; btnSend: flatButton(btnDefFlat) { color: btnYesColor; @@ -2186,7 +2188,7 @@ mediaviewLoader: size(78px, 33px); mediaviewLoaderPoint: size(9px, 9px); mediaviewLoaderSkip: 9px; -minPhotoSize: 100px; +minPhotoSize: 104px; maxMediaSize: 420px; maxStickerSize: 256px; diff --git a/Telegram/SourceFiles/gui/images.h b/Telegram/SourceFiles/gui/images.h index 384da3da1..92f7115bc 100644 --- a/Telegram/SourceFiles/gui/images.h +++ b/Telegram/SourceFiles/gui/images.h @@ -65,7 +65,7 @@ public: const QPixmap &pixBlurred(int32 w = 0, int32 h = 0) const; const QPixmap &pixColored(const style::color &add, int32 w = 0, int32 h = 0) const; const QPixmap &pixBlurredColored(const style::color &add, int32 w = 0, int32 h = 0) const; - const QPixmap &pixSingle(int32 w, int32 y, int32 outerw, int32 outerh) const; + const QPixmap &pixSingle(int32 w, int32 h, int32 outerw, int32 outerh) const; const QPixmap &pixBlurredSingle(int32 w, int32 h, int32 outerw, int32 outerh) const; QPixmap pixNoCache(int32 w = 0, int32 h = 0, bool smooth = false, bool blurred = false, bool rounded = false, int32 outerw = -1, int32 outerh = -1) const; QPixmap pixColoredNoCache(const style::color &add, int32 w = 0, int32 h = 0, bool smooth = false) const; diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 7713a5708..307f1dc5e 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -3182,7 +3182,7 @@ HistoryPhoto::HistoryPhoto(const MTPDphoto &photo, const QString &caption, Histo , _openl(new PhotoLink(_data)) , _pixw(1) , _pixh(1) -, _caption(st::minPhotoSize) { +, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) { if (!caption.isEmpty()) { _caption.setText(st::msgFont, caption + parent->skipBlock(), itemTextNoMonoOptions(parent)); } @@ -3193,8 +3193,7 @@ HistoryPhoto::HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width) , _data(App::feedPhoto(photo)) , _openl(new PhotoLink(_data, chat)) , _pixw(1) -, _pixh(1) -, _caption(st::minPhotoSize) { +, _pixh(1) { w = width; init(); } @@ -3225,18 +3224,21 @@ void HistoryPhoto::initDimensions(const HistoryItem *parent) { if (parent->toHistoryMessage()) { w = tw; + int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); + int32 maxActualWidth = qMax(w, minWidth); + _maxw = qMax(maxActualWidth, th); + _minh = qMax(th, int32(st::minPhotoSize)); + if (bubble) { + maxActualWidth += st::mediaPadding.left() + st::mediaPadding.right(); + _maxw += st::mediaPadding.left() + st::mediaPadding.right(); + _minh += st::mediaPadding.top() + st::mediaPadding.bottom(); + if (!_caption.isEmpty()) { + _minh += st::mediaCaptionSkip + _caption.countHeight(maxActualWidth - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom(); + } + } } else { th = w; // square chat photo updates - } - _maxw = qMax(qMax(w, int32(st::minPhotoSize)), th); - _minh = qMax(th, int32(st::minPhotoSize)); - if (bubble) { - _maxw += st::mediaPadding.left() + st::mediaPadding.right(); - _minh += st::mediaPadding.top() + st::mediaPadding.bottom(); - if (!_caption.isEmpty()) { - _maxw = qMax(_maxw, st::msgPadding.left() + _caption.maxWidth() + st::msgPadding.right()); - _minh += st::mediaCaptionSkip + _caption.minHeight() + st::msgPadding.bottom(); - } + _maxw = _minh = w; } } @@ -3269,7 +3271,9 @@ int32 HistoryPhoto::resize(int32 width, const HistoryItem *parent) { } if (_pixw < 1) _pixw = 1; if (_pixh < 1) _pixh = 1; - w = qMax(_pixw, int16(st::minPhotoSize)); + + int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); + w = qMax(_pixw, int16(minWidth)); _height = qMax(_pixh, int16(st::minPhotoSize)); if (bubble) { _height += st::mediaPadding.top() + st::mediaPadding.bottom(); @@ -3399,7 +3403,7 @@ void HistoryPhoto::draw(Painter &p, const HistoryItem *parent, const QRect &r, b pix = _data->thumb->pixBlurredSingle(_pixw, _pixh, width, height); } - p.drawPixmap(skipx, skipy, pix); + p.drawPixmapLeft(skipx, skipy, w, pix); if (!full) { uint64 dt = itemAnimations().animate(parent, ms); int32 cnt = int32(st::photoLoaderCnt), period = int32(st::photoLoaderPeriod), t = dt % period, delta = int32(st::photoLoaderDelta); @@ -3422,7 +3426,7 @@ void HistoryPhoto::draw(Painter &p, const HistoryItem *parent, const QRect &r, b } if (selected) { - App::roundRect(p, skipx, skipy, width, height, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); + App::roundRect(p, rtlrect(skipx, skipy, width, height, w), textstyleCurrent()->selectOverlay, SelectedOverlayCorners); } // date @@ -3481,232 +3485,53 @@ QString formatDurationAndSizeText(qint64 duration, qint64 size) { return lng_duration_and_size(lt_duration, formatDurationText(duration), lt_size, formatSizeText(size)); } +QString formatGifAndSizeText(qint64 size) { + return lng_duration_and_size(lt_duration, qsl("GIF"), lt_size, formatSizeText(size)); +} + QString formatPlayedText(qint64 played, qint64 duration) { return lng_duration_played(lt_played, formatDurationText(played), lt_duration, formatDurationText(duration)); } -HistoryVideo::HistoryVideo(const MTPDvideo &video, const QString &caption, HistoryItem *parent) : HistoryMedia() -, data(App::feedVideo(video)) -, _openl(new VideoOpenLink(data)) -, _savel(new VideoSaveLink(data)) -, _cancell(new VideoCancelLink(data)) -, _caption(st::minPhotoSize) -, _dldDone(0) -, _uplDone(0) { - if (!caption.isEmpty()) { - _caption.setText(st::msgFont, caption + parent->skipBlock(), itemTextNoMonoOptions(parent)); +namespace { + QString documentName(DocumentData *document) { + SongData *song = document->song(); + if (!song || (song->title.isEmpty() && song->performer.isEmpty())) return document->name; + + if (song->performer.isEmpty()) return song->title; + + return song->performer + QString::fromUtf8(" \xe2\x80\x93 ") + (song->title.isEmpty() ? qsl("Unknown Track") : song->title); } - _size = formatDurationAndSizeText(data->duration, data->size); - - data->thumb->load(); - - int32 tw = data->thumb->width(), th = data->thumb->height(); - if (data->thumb->isNull() || !tw || !th) { - _thumbw = 0; - } else if (tw > th) { - _thumbw = (tw * st::mediaThumbSize) / th; - } else { - _thumbw = st::mediaThumbSize; - } -} - -void HistoryVideo::initDimensions(const HistoryItem *parent) { - if (_caption.hasSkipBlock()) _caption.setSkipBlock(parent->skipBlockWidth(), parent->skipBlockHeight()); - - _maxw = st::msgMaxWidth; - int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right(); - - _minh = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom(); - if (_caption.isEmpty()) { - _height = _minh; - } else { - _minh += st::webPagePhotoSkip + _caption.minHeight(); - } -} - -void HistoryVideo::regItem(HistoryItem *item) { - App::regVideoItem(data, item); -} - -void HistoryVideo::unregItem(HistoryItem *item) { - App::unregVideoItem(data, item); -} - -const QString HistoryVideo::inDialogsText() const { - return _caption.isEmpty() ? lang(lng_in_dlg_video) : _caption.original(0, 0xFFFF, Text::ExpandLinksNone); -} - -const QString HistoryVideo::inHistoryText() const { - return qsl("[ ") + lang(lng_in_dlg_video) + (_caption.isEmpty() ? QString() : (qsl(", ") + _caption.original(0, 0xFFFF, Text::ExpandLinksAll))) + qsl(" ]"); -} - -bool HistoryVideo::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const { - int32 height = _height; - if (width < 0) { - width = w; - } else if (!_caption.isEmpty()) { - height = countHeight(parent, width); - } - if (width >= _maxw) { - width = _maxw; - } - return (x >= 0 && y >= 0 && x < width && y < height); -} - -int32 HistoryVideo::countHeight(const HistoryItem *parent, int32 width) const { - if (_caption.isEmpty()) return _height; - - if (width < 0) width = w; - if (width >= _maxw) { - width = _maxw; + int32 videoMaxStatusWidth(VideoData *video) { + int32 result = st::normalFont->width(formatDownloadText(video->size, video->size)); + result = qMax(result, st::normalFont->width(formatDurationAndSizeText(video->duration, video->size))); + return result; } - int32 h = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom(); - if (!_caption.isEmpty()) { - int32 textw = width - st::mediaPadding.left() - st::mediaPadding.right(); - h += st::webPagePhotoSkip + _caption.countHeight(textw); - } - return h; -} - -void HistoryVideo::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width) const { - int32 height = _height; - if (width < 0) { - width = w; - } else if (!_caption.isEmpty()) { - height = countHeight(parent, width); - } - if (width < 1) return; - - int skipy = 0, replyFrom = 0, fwdFrom = 0; - - bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel, hovered, pressed; - if (width >= _maxw) { - width = _maxw; + int32 audioMaxStatusWidth(AudioData *audio) { + int32 result = st::normalFont->width(formatDownloadText(audio->size, audio->size)); + result = qMax(result, st::normalFont->width(formatPlayedText(audio->duration, audio->duration))); + result = qMax(result, st::normalFont->width(formatDurationAndSizeText(audio->duration, audio->size))); + return result; } - bool inDate = parent->pointInTime(width, height, x, y, InfoDisplayDefault); - if (inDate) { - state = HistoryInDateCursorState; - } - - int32 tw = width - st::mediaPadding.left() - st::mediaPadding.right(); - if (x >= st::mediaPadding.left() && y >= skipy + st::mediaPadding.top() && x < st::mediaPadding.left() + tw && y < skipy + st::mediaPadding.top() + st::mediaThumbSize && !data->loader && data->access) { - lnk = _openl; - return; - } - if (!_caption.isEmpty() && x >= st::mediaPadding.left() && x < st::mediaPadding.left() + tw && y >= skipy + st::mediaPadding.top() + st::mediaThumbSize + st::webPagePhotoSkip) { - bool inText = false; - _caption.getState(lnk, inText, x - st::mediaPadding.left(), y - skipy - st::mediaPadding.top() - st::mediaThumbSize - st::webPagePhotoSkip, tw); - state = inDate ? HistoryInDateCursorState : (inText ? HistoryInTextCursorState : HistoryDefaultCursorState); - } -} - -HistoryMedia *HistoryVideo::clone() const { - return new HistoryVideo(*this); -} - -void HistoryVideo::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { - if (w < st::msgPadding.left() + st::msgPadding.right() + 1) return; - int32 width = w, height = _height, skipx = 0, skipy = 0; - - data->thumb->checkload(); - - bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel, hovered, pressed; - if (width >= _maxw) { - width = _maxw; - } - - style::color bg(outbg ? (selected ? st::msgOutBgSelected : st::msgOutBg) : (selected ? st::msgInBgSelected : st::msgInBg)); - style::color sh(outbg ? (selected ? st::msgOutShadowSelected : st::msgOutShadow) : (selected ? st::msgInShadowSelected : st::msgInShadow)); - RoundCorners cors(selected ? (outbg ? MessageOutSelectedCorners : MessageInSelectedCorners) : (outbg ? MessageOutCorners : MessageInCorners)); - App::roundRect(p, 0, 0, width, height, bg, cors, &sh); - - //if (_thumbw) { - // p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), data->thumb->pixSingle(_thumbw, 0, st::mediaThumbSize, st::mediaThumbSize)); - //} else { - // p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), App::sprite(), (outbg ? st::mediaDocOutImg : st::mediaDocInImg)); - //} - if (selected) { - App::roundRect(p, st::mediaPadding.left(), skipy + st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); - } - - int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right(); - int32 twidth = width - tleft - st::mediaPadding.right(); - int32 secondwidth = width - tleft - st::msgPadding.right() - parent->skipBlockWidth(); - - p.setFont(st::normalFont->f); - p.setPen(st::black->c); - p.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaNameTop + st::normalFont->ascent, lang(lng_media_video)); - - QString statusText; - - style::color status(outbg ? (selected ? st::mediaOutFgSelected : st::mediaOutFg) : (selected ? st::mediaInFgSelected : st::mediaInFg)); - p.setPen(status->p); - - if (data->loader) { - int32 offset = data->loader->currentOffset(); - if (_dldTextCache.isEmpty() || _dldDone != offset) { - _dldDone = offset; - _dldTextCache = formatDownloadText(_dldDone, data->size); - } - statusText = _dldTextCache; - } else { - if (data->status == FileUploadFailed || data->status == FileDownloadFailed) { - statusText = lang(lng_attach_failed); - } else if (data->status == FileUploading) { - if (_uplTextCache.isEmpty() || _uplDone != data->uploadOffset) { - _uplDone = data->uploadOffset; - _uplTextCache = formatDownloadText(_uplDone, data->size); - } - statusText = _uplTextCache; + int32 documentMaxStatusWidth(DocumentData *document) { + int32 result = st::normalFont->width(formatDownloadText(document->size, document->size)); + if (SongData *song = document->song()) { + result = qMax(result, st::normalFont->width(formatPlayedText(song->duration, song->duration))); + result = qMax(result, st::normalFont->width(formatDurationAndSizeText(song->duration, document->size))); } else { - statusText = _size; + result = qMax(result, st::normalFont->width(formatSizeText(document->size))); } + return result; } - int32 texty = skipy + st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::normalFont->height; - p.drawText(tleft, texty + st::normalFont->ascent, statusText); - if (parent->isMediaUnread()) { - int32 w = st::normalFont->width(statusText); - if (w + st::mediaUnreadSkip + st::mediaUnreadSize <= twidth) { - p.setRenderHint(QPainter::HighQualityAntialiasing, true); - p.setPen(Qt::NoPen); - p.setBrush((outbg ? (selected ? st::mediaOutUnreadFgSelected : st::mediaOutUnreadFg) : (selected ? st::mediaInUnreadFgSelected : st::mediaInUnreadFg))->b); - p.drawEllipse(QRect(tleft + w + st::mediaUnreadSkip, texty + ((st::normalFont->height - st::mediaUnreadSize) / 2), st::mediaUnreadSize, st::mediaUnreadSize)); - p.setRenderHint(QPainter::HighQualityAntialiasing, false); - } - } - if (!_caption.isEmpty()) { - p.setPen(st::black->p); - _caption.draw(p, st::mediaPadding.left(), skipy + st::mediaPadding.top() + st::mediaThumbSize + st::webPagePhotoSkip, width - st::mediaPadding.left() - st::mediaPadding.right()); - } -} -int32 HistoryVideo::resize(int32 width, const HistoryItem *parent) { - w = qMin(width, _maxw); - if (_caption.isEmpty()) return _height; - - _height = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom(); - if (!_caption.isEmpty()) { - int32 textw = w - st::mediaPadding.left() - st::mediaPadding.right(); - _height += st::webPagePhotoSkip + _caption.countHeight(textw); + int32 gifMaxStatusWidth(DocumentData *document) { + int32 result = st::normalFont->width(formatDownloadText(document->size, document->size)); + result = qMax(result, st::normalFont->width(formatGifAndSizeText(document->size))); + return result; } - return _height; -} - -ImagePtr HistoryVideo::replyPreview() { - if (data->replyPreview->isNull() && !data->thumb->isNull()) { - if (data->thumb->loaded()) { - int w = data->thumb->width(), h = data->thumb->height(); - if (w <= 0) w = 1; - if (h <= 0) h = 1; - data->replyPreview = ImagePtr(w > h ? data->thumb->pix(w * st::msgReplyBarSize.height() / h, st::msgReplyBarSize.height()) : data->thumb->pix(st::msgReplyBarSize.height()), "PNG"); - } else { - data->thumb->load(); - } - } - return data->replyPreview; } HistoryFileMedia::HistoryFileMedia() : HistoryMedia() @@ -3797,33 +3622,322 @@ HistoryFileMedia::~HistoryFileMedia() { } } -namespace { - QString documentName(DocumentData *document) { - SongData *song = document->song(); - if (!song || (song->title.isEmpty() && song->performer.isEmpty())) return document->name; - - if (song->performer.isEmpty()) return song->title; - - return song->performer + QString::fromUtf8(" \xe2\x80\x93 ") + (song->title.isEmpty() ? qsl("Unknown Track") : song->title); +HistoryVideo::HistoryVideo(const MTPDvideo &video, const QString &caption, HistoryItem *parent) : HistoryFileMedia() +, _data(App::feedVideo(video)) +, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) +, _thumbw(1) { + if (!caption.isEmpty()) { + _caption.setText(st::msgFont, caption + parent->skipBlock(), itemTextNoMonoOptions(parent)); } - int32 documentMaxStatusWidth(DocumentData *document) { - int32 result = st::normalFont->width(formatDownloadText(document->size, document->size)); - if (SongData *song = document->song()) { - result = qMax(result, st::normalFont->width(formatPlayedText(song->duration, song->duration))); - result = qMax(result, st::normalFont->width(formatDurationAndSizeText(song->duration, document->size))); - } else { - result = qMax(result, st::normalFont->width(formatSizeText(document->size))); + setLinks(new VideoOpenLink(_data), new VideoSaveLink(_data), new VideoCancelLink(_data)); + + setStatusSize(FileStatusSizeReady); + + _data->thumb->load(); +} + +void HistoryVideo::initDimensions(const HistoryItem *parent) { + bool bubble = parent->hasBubble(); + + if (_caption.hasSkipBlock()) { + _caption.setSkipBlock(parent->skipBlockWidth(), parent->skipBlockHeight()); + } + + int32 tw = convertScale(_data->thumb->width()), th = convertScale(_data->thumb->height()); + 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(); + } + + w = _thumbw = qMax(tw, 1); + int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); + minWidth = qMax(minWidth, videoMaxStatusWidth(_data) + 2 * int32(st::msgDateImgDelta + st::msgDateImgPadding.x())); + _maxw = qMax(w, minWidth); + _minh = qMax(th, int32(st::minPhotoSize)); + if (bubble) { + _maxw += st::mediaPadding.left() + st::mediaPadding.right(); + _minh += st::mediaPadding.top() + st::mediaPadding.bottom(); + if (!_caption.isEmpty()) { + _minh += st::mediaCaptionSkip + _caption.countHeight(_maxw - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom(); } - return result; + } +} + +void HistoryVideo::setStatusSize(int32 newSize) const { + HistoryFileMedia::setStatusSize(newSize, _data->size, _data->duration, 0); +} + +void HistoryVideo::updateStatusText(const HistoryItem *parent) const { + bool showPause = false; + int32 statusSize = 0, realDuration = 0; + if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) { + statusSize = FileStatusSizeFailed; + } else if (_data->status == FileUploading) { + statusSize = _data->uploadOffset; + } else if (_data->loader) { + statusSize = _data->loader->currentOffset(); + } else if (!_data->already().isEmpty()) { + statusSize = FileStatusSizeLoaded; + } else { + statusSize = FileStatusSizeReady; + } + if (statusSize != _statusSize) { + setStatusSize(statusSize); + } +} + +void HistoryVideo::regItem(HistoryItem *item) { + App::regVideoItem(_data, item); +} + +void HistoryVideo::unregItem(HistoryItem *item) { + App::unregVideoItem(_data, item); +} + +const QString HistoryVideo::inDialogsText() const { + return _caption.isEmpty() ? lang(lng_in_dlg_video) : _caption.original(0, 0xFFFF, Text::ExpandLinksNone); +} + +const QString HistoryVideo::inHistoryText() const { + return qsl("[ ") + lang(lng_in_dlg_video) + (_caption.isEmpty() ? QString() : (qsl(", ") + _caption.original(0, 0xFFFF, Text::ExpandLinksAll))) + qsl(" ]"); +} + +bool HistoryVideo::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const { + if (width < 0) width = w; + return (x >= 0 && y >= 0 && x < width && y < _height); +} + +int32 HistoryVideo::countHeight(const HistoryItem *parent, int32 width) const { + if (width < 0) width = w; + if (width >= _maxw) { + width = _maxw; } - int32 audioMaxStatusWidth(AudioData *audio) { - int32 result = st::normalFont->width(formatDownloadText(audio->size, audio->size)); - result = qMax(result, st::normalFont->width(formatPlayedText(audio->duration, audio->duration))); - result = qMax(result, st::normalFont->width(formatDurationAndSizeText(audio->duration, audio->size))); - return result; + bool bubble = parent->hasBubble(); + + int32 tw = convertScale(_data->thumb->width()), th = convertScale(_data->thumb->height()); + 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 (bubble) { + width -= st::mediaPadding.left() + st::mediaPadding.right(); + } + if (width < tw) { + th = qRound((width / float64(tw)) * th); + tw = width; + } + int32 h = qMax(th, int32(st::minPhotoSize)); + if (bubble) { + tw += st::mediaPadding.left() + st::mediaPadding.right(); + h += st::mediaPadding.top() + st::mediaPadding.bottom(); + if (!_caption.isEmpty()) { + h += st::mediaCaptionSkip + _caption.countHeight(tw - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom(); + } + } + return h; +} + +void HistoryVideo::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width) const { + bool bubble = parent->hasBubble(); + + if (width < 0) width = w; + if (width < 1) return; + + int skipx = 0, skipy = 0, height = _height; + if (bubble) { + skipx = st::mediaPadding.left(); + skipy = st::mediaPadding.top(); + if (!_caption.isEmpty()) { + int32 captionw = width - st::msgPadding.left() - st::msgPadding.right(); + height -= _caption.countHeight(captionw) + st::msgPadding.bottom(); + if (x >= st::msgPadding.left() && y >= height && x < st::msgPadding.left() + captionw && y < _height) { + bool inText = false; + _caption.getState(lnk, inText, x - st::msgPadding.left(), y - height, captionw); + state = inText ? HistoryInTextCursorState : HistoryDefaultCursorState; + } + height -= st::mediaCaptionSkip; + } + width -= st::mediaPadding.left() + st::mediaPadding.right(); + height -= skipy + st::mediaPadding.bottom(); + } + if (x >= skipx && y >= skipy && x < skipx + width && y < skipy + height) { + lnk = _data->already().isEmpty() ? (_data->loader ? _cancell : _savel) : _openl; + if (_caption.isEmpty()) { + int32 fullRight = skipx + width, fullBottom = skipy + height; + bool inDate = parent->pointInTime(fullRight, fullBottom, x, y, InfoDisplayOverImage); + if (inDate) { + state = HistoryInDateCursorState; + } + } + return; + } +} + +HistoryMedia *HistoryVideo::clone() const { + return new HistoryVideo(*this); +} + +void HistoryVideo::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { + if (w < st::msgPadding.left() + st::msgPadding.right() + 1) return; + int32 width = w, height = _height, skipx = 0, skipy = 0; + int32 captionw = width - st::msgPadding.left() - st::msgPadding.right(); + + bool bubble = parent->hasBubble(); + bool fromChannel = parent->fromChannel(), out = parent->out(), outbg = out && !fromChannel; + + if (_data->loader) { + ensureAnimation(parent); + if (!_animation->radial.animating()) { + _animation->radial.start(_data->progress()); + } + } + updateStatusText(parent); + bool radial = isRadialAnimation(ms); + + if (bubble) { + skipx = st::mediaPadding.left(); + skipy = st::mediaPadding.top(); + + width -= st::mediaPadding.left() + st::mediaPadding.right(); + height -= skipy + st::mediaPadding.bottom(); + if (!_caption.isEmpty()) { + height -= st::mediaCaptionSkip + _caption.countHeight(captionw) + st::msgPadding.bottom(); + } + } else { + App::roundShadow(p, 0, 0, width, _height, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners); + } + _data->thumb->checkload(); + + QRect rthumb(rtlrect(skipx, skipy, width, height, w)); + + QPixmap pix = _data->thumb->pixBlurredSingle(_thumbw, 0, width, height); + p.drawPixmap(rthumb.topLeft(), pix); + if (selected) { + App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); + } + + 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 (_animation && _animation->_a_thumbOver.animating()) { + _animation->_a_thumbOver.step(ms); + float64 over = _animation->a_thumbOver.current(); + p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over)); + p.setBrush(st::black); + } else { + bool over = textlnkDrawOver(_data->loader ? _cancell : _savel); + p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); + } + + p.setRenderHint(QPainter::HighQualityAntialiasing); + p.drawEllipse(inner); + p.setRenderHint(QPainter::HighQualityAntialiasing, false); + + if (!selected && _animation) { + p.setOpacity(1); + } + + style::sprite icon; + if (!_data->already().isEmpty()) { + icon = (selected ? st::msgFileInPlaySelected : st::msgFileInPlay); + } else if (_data->loader) { + icon = (selected ? st::msgFileInCancelSelected : st::msgFileInCancel); + } else { + icon = (selected ? st::msgFileInDownloadSelected : st::msgFileInDownload); + } + p.drawSpriteCenter(inner, icon); + if (radial) { + QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine))); + _animation->radial.draw(p, rinner, selected ? st::msgInBgSelected : st::msgInBg); + } + + int32 statusX = skipx + st::msgDateImgDelta + st::msgDateImgPadding.x(), statusY = skipy + st::msgDateImgDelta + st::msgDateImgPadding.y(); + int32 statusW = st::normalFont->width(_statusText) + 2 * st::msgDateImgPadding.x(); + int32 statusH = st::normalFont->height + 2 * st::msgDateImgPadding.y(); + App::roundRect(p, rtlrect(statusX - st::msgDateImgPadding.x(), statusY - st::msgDateImgPadding.y(), statusW, statusH, w), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); + p.setFont(st::normalFont); + p.setPen(st::white); + p.drawTextLeft(statusX, statusY, w, _statusText, statusW - 2 * st::msgDateImgPadding.x()); + + // date + QString time(parent->timeText()); + if (_caption.isEmpty()) { + int32 fullRight = skipx + width, fullBottom = skipy + height; + parent->drawInfo(p, fullRight, fullBottom, 2 * skipx + width, selected, InfoDisplayOverImage); + } else { + p.setPen(st::black); + _caption.draw(p, st::msgPadding.left(), skipy + height + st::mediaPadding.bottom() + st::mediaCaptionSkip, captionw); + } +} + +int32 HistoryVideo::resize(int32 width, const HistoryItem *parent) { + bool bubble = parent->hasBubble(); + + int32 tw = convertScale(_data->thumb->width()), th = convertScale(_data->thumb->height()); + 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 (bubble) { + width -= st::mediaPadding.left() + st::mediaPadding.right(); + } + if (width < tw) { + th = qRound((width / float64(tw)) * th); + tw = width; + } + w = _thumbw = tw; + + int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); + minWidth = qMax(minWidth, videoMaxStatusWidth(_data) + 2 * int32(st::msgDateImgDelta + st::msgDateImgPadding.x())); + w = qMax(w, minWidth); + + _height = qMax(th, int32(st::minPhotoSize)); + if (bubble) { + w += st::mediaPadding.left() + st::mediaPadding.right(); + _height += st::mediaPadding.top() + st::mediaPadding.bottom(); + if (!_caption.isEmpty()) { + int32 captionw = w - st::msgPadding.left() - st::msgPadding.right(); + _height += st::mediaCaptionSkip + _caption.countHeight(captionw) + st::msgPadding.bottom(); + } + } + return _height; +} + +ImagePtr HistoryVideo::replyPreview() { + if (_data->replyPreview->isNull() && !_data->thumb->isNull()) { + if (_data->thumb->loaded()) { + int w = _data->thumb->width(), h = _data->thumb->height(); + if (w <= 0) w = 1; + if (h <= 0) h = 1; + _data->replyPreview = ImagePtr(w > h ? _data->thumb->pix(w * st::msgReplyBarSize.height() / h, st::msgReplyBarSize.height()) : _data->thumb->pix(st::msgReplyBarSize.height()), "PNG"); + } else { + _data->thumb->load(); + } + } + return _data->replyPreview; } HistoryAudio::HistoryAudio(const MTPDaudio &audio) : HistoryFileMedia() diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 85e029dc3..a87b91f78 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -1303,61 +1303,6 @@ QString formatDurationText(qint64 duration); QString formatDurationAndSizeText(qint64 duration, qint64 size); QString formatPlayedText(qint64 played, qint64 duration); -class HistoryVideo : public HistoryMedia { -public: - - HistoryVideo(const MTPDvideo &video, const QString &caption, HistoryItem *parent); - void initDimensions(const HistoryItem *parent); - - void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const; - int32 resize(int32 width, const HistoryItem *parent); - HistoryMediaType type() const { - return MediaTypeVideo; - } - const QString inDialogsText() const; - const QString inHistoryText() const; - bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const; - int32 countHeight(const HistoryItem *parent, int32 width = -1) const; - void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const; - bool uploading() const { - return (data->status == FileUploading); - } - HistoryMedia *clone() const; - - void regItem(HistoryItem *item); - void unregItem(HistoryItem *item); - - bool hasReplyPreview() const { - return !data->thumb->isNull(); - } - ImagePtr replyPreview(); - - bool needsBubble(const HistoryItem *parent) const { - return !_caption.isEmpty() || parent->toHistoryReply(); - } - bool customInfoLayout() const { - return _caption.isEmpty(); - } - bool hideFromName() const { - return true; - } - bool hideForwardedFrom() const { - return true; - } - -private: - VideoData *data; - TextLinkPtr _openl, _savel, _cancell; - - Text _caption; - - QString _size; - int32 _thumbw; - - mutable QString _dldTextCache, _uplTextCache; - mutable int32 _dldDone, _uplDone; -}; - class HistoryFileMedia : public HistoryMedia { public: @@ -1414,6 +1359,70 @@ protected: }; +class HistoryVideo : public HistoryFileMedia { +public: + + HistoryVideo(const MTPDvideo &video, const QString &caption, HistoryItem *parent); + void initDimensions(const HistoryItem *parent); + + void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const; + int32 resize(int32 width, const HistoryItem *parent); + HistoryMediaType type() const { + return MediaTypeVideo; + } + const QString inDialogsText() const; + const QString inHistoryText() const; + bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const; + int32 countHeight(const HistoryItem *parent, int32 width = -1) const; + void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const; + bool uploading() const { + return (_data->status == FileUploading); + } + HistoryMedia *clone() const; + + void regItem(HistoryItem *item); + void unregItem(HistoryItem *item); + + bool hasReplyPreview() const { + return !_data->thumb->isNull(); + } + ImagePtr replyPreview(); + + bool needsBubble(const HistoryItem *parent) const { + return !_caption.isEmpty() || parent->toHistoryReply(); + } + bool customInfoLayout() const { + return _caption.isEmpty(); + } + bool hideFromName() const { + return true; + } + bool hideForwardedFrom() const { + return true; + } + +protected: + + float64 dataProgress() const { + return _data->progress(); + } + bool dataFinished() const { + return !_data->loader; + } + bool dataLoaded() const { + return !_data->already().isEmpty(); + } + +private: + VideoData *_data; + Text _caption; + int32 _thumbw; + + void setStatusSize(int32 newSize) const; + void updateStatusText(const HistoryItem *parent) const; + +}; + class HistoryAudio : public HistoryFileMedia { public: @@ -1463,6 +1472,7 @@ protected: private: AudioData *_data; + void setStatusSize(int32 newSize, qint64 realDuration = 0) const; bool updateStatusText(const HistoryItem *parent) const; // returns showPause diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index ecad9645f..1443a40cc 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -719,6 +719,18 @@ void VideoData::save(const QString &toFile) { loader->connect(loader, SIGNAL(progress(mtpFileLoader*)), App::main(), SLOT(videoLoadProgress(mtpFileLoader*))); loader->connect(loader, SIGNAL(failed(mtpFileLoader*, bool)), App::main(), SLOT(videoLoadFailed(mtpFileLoader*, bool))); loader->start(); + + notifyLayoutChanged(); +} + +void VideoData::notifyLayoutChanged() const { + const VideoItems &items(App::videoItems()); + VideoItems::const_iterator i = items.constFind(const_cast(this)); + if (i != items.cend()) { + for (HistoryItemsMap::const_iterator j = i->cbegin(), e = i->cend(); j != e; ++j) { + Notify::historyItemLayoutChanged(j.key()); + } + } } QString VideoData::already(bool check) { diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index 67c767cf2..f9dc10b82 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -809,6 +809,8 @@ struct VideoData { l->cancel(); l->deleteLater(); l->rpcInvalidate(); + + notifyLayoutChanged(); } _location = FileLocation(); if (!beforeDownload) { @@ -824,11 +826,19 @@ struct VideoData { loader->deleteLater(); loader->rpcInvalidate(); loader = 0; + + notifyLayoutChanged(); } + void notifyLayoutChanged() const; + QString already(bool check = false); const FileLocation &location(bool check = false); + float64 progress() const { + return loader ? loader->currentProgress() : ((status == FileDownloadFailed || _location.name().isEmpty()) ? 0 : 1); + } + VideoId id; uint64 access; int32 date;