diff --git a/QTCREATOR.md b/QTCREATOR.md index 13baf5442e..d963f9badf 100644 --- a/QTCREATOR.md +++ b/QTCREATOR.md @@ -61,7 +61,7 @@ In Terminal go to **/home/user/TBuild/Libraries** and run sudo apt-get -y --force-yes install autoconf automake build-essential libass-dev libfreetype6-dev libgpac-dev libsdl1.2-dev libtheora-dev libtool libva-dev libvdpau-dev libvorbis-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev pkg-config texi2html zlib1g-dev sudo apt-get install yasm - ./configure --prefix=/usr/local --disable-programs --disable-pthreads --disable-doc --disable-everything --enable-libopus --enable-decoder=aac --enable-decoder=aac_latm --enable-decoder=aasc --enable-decoder=flac --enable-decoder=gif --enable-decoder=h264 --enable-decoder=h264_vdpau --enable-decoder=mp1 --enable-decoder=mp1float --enable-decoder=mp2 --enable-decoder=mp2float --enable-decoder=mp3 --enable-decoder=mp3adu --enable-decoder=mp3adufloat --enable-decoder=mp3float --enable-decoder=mp3on4 --enable-decoder=mp3on4float --enable-decoder=mpeg4 --enable-decoder=mpeg4_vdpau --enable-decoder=msmpeg4v2 --enable-decoder=msmpeg4v3 --enable-decoder=opus --enable-decoder=vorbis --enable-decoder=wavpack --enable-decoder=wmalossless --enable-decoder=wmapro --enable-decoder=wmav1 --enable-decoder=wmav2 --enable-decoder=wmavoice --enable-encoder=libopus --enable-hwaccel=h264_vaapi --enable-hwaccel=h264_vdpau --enable-hwaccel=mpeg4_vaapi --enable-hwaccel=mpeg4_vdpau --enable-parser=aac --enable-parser=aac_latm --enable-parser=flac --enable-parser=h264 --enable-parser=mpeg4video --enable-parser=mpegaudio --enable-parser=opus --enable-parser=vorbis --enable-demuxer=aac --enable-demuxer=flac --enable-demuxer=gif --enable-demuxer=h264 --enable-demuxer=mov --enable-demuxer=mp3 --enable-demuxer=ogg --enable-demuxer=wav --enable-muxer=ogg --enable-muxer=opus + ./configure --prefix=/usr/local --disable-programs --disable-doc --disable-pthreads --disable-mmx --disable-everything --enable-libopus --enable-decoder=aac --enable-decoder=aac_latm --enable-decoder=aasc --enable-decoder=flac --enable-decoder=gif --enable-decoder=h264 --enable-decoder=h264_vdpau --enable-decoder=mp1 --enable-decoder=mp1float --enable-decoder=mp2 --enable-decoder=mp2float --enable-decoder=mp3 --enable-decoder=mp3adu --enable-decoder=mp3adufloat --enable-decoder=mp3float --enable-decoder=mp3on4 --enable-decoder=mp3on4float --enable-decoder=mpeg4 --enable-decoder=mpeg4_vdpau --enable-decoder=msmpeg4v2 --enable-decoder=msmpeg4v3 --enable-decoder=opus --enable-decoder=vorbis --enable-decoder=wavpack --enable-decoder=wmalossless --enable-decoder=wmapro --enable-decoder=wmav1 --enable-decoder=wmav2 --enable-decoder=wmavoice --enable-encoder=libopus --enable-hwaccel=h264_vaapi --enable-hwaccel=h264_vdpau --enable-hwaccel=mpeg4_vaapi --enable-hwaccel=mpeg4_vdpau --enable-parser=aac --enable-parser=aac_latm --enable-parser=flac --enable-parser=h264 --enable-parser=mpeg4video --enable-parser=mpegaudio --enable-parser=opus --enable-parser=vorbis --enable-demuxer=aac --enable-demuxer=flac --enable-demuxer=gif --enable-demuxer=h264 --enable-demuxer=mov --enable-demuxer=mp3 --enable-demuxer=ogg --enable-demuxer=wav --enable-muxer=ogg --enable-muxer=opus make sudo make install diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index 155785981b..a1f7a785cd 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -1197,6 +1197,18 @@ msgFileRedColor: #e47272; msgFileYellowColor: #efc274; msgFileGreenColor: #61b96e; msgFileBlueColor: #72b1df; +msgFileRedDark: #cd5b5e; +msgFileYellowDark: #e6a561; +msgFileGreenDark: #4da859; +msgFileBlueDark: #5c9ece; +msgFileRedOver: #c35154; +msgFileYellowOver: #dc9c5a; +msgFileGreenOver: #44a050; +msgFileBlueOver: #5294c4; +msgFileRedSelected: #9f6a82; +msgFileYellowSelected: #b19d84; +msgFileGreenSelected: #46a07e; +msgFileBlueSelected: #5099d0; msgFileMenuSize: size(36px, 36px); msgFileSize: 44px; @@ -2161,6 +2173,14 @@ overviewPhotoCheck: sprite(245px, 308px, 32px, 32px); overviewPhotoChecked: sprite(278px, 308px, 32px, 32px); overviewPhotoSelectOverlay: #0a7bb03f; +overviewFilePadding: margins(0px, 3px, 16px, 3px); +overviewFileSize: 70px; +overviewFileNameTop: 7px; +overviewFileStatusTop: 27px; +overviewFileDateTop: 49px; +overviewFileChecked: #2fa9e2; +overviewFileCheck: #00000066; + // Mac specific macAccessory: size(450, 90); @@ -2346,11 +2366,12 @@ playlistPadding: 10px; linksSearchMargin: margins(20px, 20px, 20px, 0px); linksMaxWidth: 520px; linksLetterFont: font(24px); -linksMargin: 5px; +linksMargin: margins(0px, 7px, 0px, 5px); +linksTextTop: 6px; linksBorder: 1px; linksBorderFg: #eaeaea; -linksDateColor: #000; -linksDateMargin: 15px; +linksDateColor: #808080; +linksDateMargin: margins(0px, 15px, 0px, 2px); linksPhotoCheck: sprite(184px, 196px, 16px, 16px); linksPhotoChecked: sprite(168px, 196px, 16px, 16px); diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index 9313e0b851..ca89de07c0 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -708,7 +708,7 @@ void Application::checkMapVersion() { if (cDevVersion() && Local::oldMapVersion() < 9016) { // versionFeatures = QString::fromUtf8("\xe2\x80\x94 Sticker management: manually rearrange your sticker packs, pack order is now synced across all your devices\n\xe2\x80\x94 Click and hold on a sticker to preview it before sending\n\xe2\x80\x94 New context menu for chats in chats list\n\xe2\x80\x94 Support for all existing emoji");// .replace('@', qsl("@") + QChar(0x200D)); versionFeatures = lng_new_version_text(lt_gifs_link, qsl("https://telegram.org/blog/gif-revolution"), lt_bots_link, qsl("https://telegram.org/blog/inline-bots")).trimmed(); - } else if (Local::oldMapVersion() < 9015) { + } else if (Local::oldMapVersion() < 9016) { versionFeatures = lng_new_version_text(lt_gifs_link, qsl("https://telegram.org/blog/gif-revolution"), lt_bots_link, qsl("https://telegram.org/blog/inline-bots")).trimmed(); } else { versionFeatures = lang(lng_new_version_minor).trimmed(); diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index b78e8b3875..e41af83661 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -20,10 +20,10 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org */ #pragma once -static const int32 AppVersion = 9015; -static const wchar_t *AppVersionStr = L"0.9.15"; +static const int32 AppVersion = 9016; +static const wchar_t *AppVersionStr = L"0.9.16"; static const bool DevVersion = false; -#define BETA_VERSION (9015007ULL) // just comment this line to build public version +//#define BETA_VERSION (9015008ULL) // just comment this line to build public version static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)"; static const wchar_t *AppName = L"Telegram Desktop"; diff --git a/Telegram/SourceFiles/dropdown.cpp b/Telegram/SourceFiles/dropdown.cpp index 0ccdfbf771..87d9fd97b2 100644 --- a/Telegram/SourceFiles/dropdown.cpp +++ b/Telegram/SourceFiles/dropdown.cpp @@ -1663,14 +1663,14 @@ void StickerPanInner::refreshStickers() { updateSelected(); } -void StickerPanInner::inlineRowsAddItem(DocumentData *savedGif, InlineResult *result, InlineRow &row, int32 &sumWidth) { +bool StickerPanInner::inlineRowsAddItem(DocumentData *savedGif, InlineResult *result, InlineRow &row, int32 &sumWidth) { LayoutInlineItem *layout = 0; if (savedGif) { layout = layoutPrepareSavedGif(savedGif, (_inlineRows.size() * MatrixRowShift) + row.items.size()); } else if (result) { layout = layoutPrepareInlineResult(result, (_inlineRows.size() * MatrixRowShift) + row.items.size()); } - if (!layout) return; + if (!layout) return false; layout->preload(); if (inlineRowFinalize(row, sumWidth, layout->fullLine())) { @@ -1678,6 +1678,7 @@ void StickerPanInner::inlineRowsAddItem(DocumentData *savedGif, InlineResult *re } row.items.push_back(layout); sumWidth += layout->maxWidth(); + return true; } bool StickerPanInner::inlineRowFinalize(InlineRow &row, int32 &sumWidth, bool force) { @@ -1770,7 +1771,7 @@ LayoutInlineItem *StickerPanInner::layoutPrepareInlineResult(InlineResult *resul layout = new LayoutInlineGif(result, 0, false); } else if (result->type == qstr("photo")) { layout = new LayoutInlinePhoto(result, 0); - } else if (result->type == qstr("web_player_video")) { + } else if (result->type == qstr("video")) { layout = new LayoutInlineWebVideo(result); } else if (result->type == qstr("article")) { layout = new LayoutInlineArticle(result, _inlineWithThumb); @@ -1901,14 +1902,14 @@ void StickerPanInner::clearInlineRowsPanel() { clearInlineRows(false); } -void StickerPanInner::refreshInlineRows(UserData *bot, const InlineResults &results, bool resultsDeleted) { +int32 StickerPanInner::refreshInlineRows(UserData *bot, const InlineResults &results, bool resultsDeleted) { _inlineBot = bot; if (results.isEmpty() && (!_inlineBot || _inlineBot->username != cInlineGifBotUsername())) { if (resultsDeleted) { clearInlineRows(true); } emit emptyInlineRows(); - return; + return 0; } if (_showingInlineItems) { @@ -1921,7 +1922,7 @@ void StickerPanInner::refreshInlineRows(UserData *bot, const InlineResults &resu _showingInlineItems = true; _showingSavedGifs = false; - int32 count = results.size(), from = validateExistingInlineRows(results); + int32 count = results.size(), from = validateExistingInlineRows(results), added = 0; if (count) { _inlineRows.reserve(count); @@ -1929,7 +1930,9 @@ void StickerPanInner::refreshInlineRows(UserData *bot, const InlineResults &resu row.items.reserve(SavedGifsMaxPerRow); int32 sumWidth = 0; for (int32 i = from; i < count; ++i) { - inlineRowsAddItem(0, results.at(i), row, sumWidth); + if (inlineRowsAddItem(0, results.at(i), row, sumWidth)) { + ++added; + } } inlineRowFinalize(row, sumWidth, true); } @@ -1943,6 +1946,8 @@ void StickerPanInner::refreshInlineRows(UserData *bot, const InlineResults &resu _lastMousePos = QCursor::pos(); updateSelected(); } + + return added; } int32 StickerPanInner::validateExistingInlineRows(const InlineResults &results) { @@ -3722,7 +3727,10 @@ void EmojiPan::inlineResultsDone(const MTPmessages_BotResults &result) { } else if (adding) { it.value()->nextOffset = QString(); } - showInlineRows(!adding); + + if (!showInlineRows(!adding)) { + it.value()->nextOffset = QString(); + } onScroll(); } @@ -3782,7 +3790,7 @@ void EmojiPan::onEmptyInlineRows() { } } -bool EmojiPan::refreshInlineRows() { +bool EmojiPan::refreshInlineRows(int32 *added) { bool clear = true; InlineCache::const_iterator i = _inlineCache.constFind(_inlineQuery); if (i != _inlineCache.cend()) { @@ -3790,12 +3798,14 @@ bool EmojiPan::refreshInlineRows() { _inlineNextOffset = i.value()->nextOffset; } if (clear) prepareShowHideCache(); - s_inner.refreshInlineRows(_inlineBot, clear ? InlineResults() : i.value()->results, false); + int32 result = s_inner.refreshInlineRows(_inlineBot, clear ? InlineResults() : i.value()->results, false); + if (added) *added = result; return !clear; } -void EmojiPan::showInlineRows(bool newResults) { - bool clear = !refreshInlineRows(); +int32 EmojiPan::showInlineRows(bool newResults) { + int32 added = 0; + bool clear = !refreshInlineRows(&added); if (newResults) s_scroll.scrollToY(0); e_switch.updateText(clear ? QString() : _inlineBot->username); @@ -3819,6 +3829,8 @@ void EmojiPan::showInlineRows(bool newResults) { onSwitch(); } } + + return added; } void EmojiPan::recountContentMaxHeight() { diff --git a/Telegram/SourceFiles/dropdown.h b/Telegram/SourceFiles/dropdown.h index 9bbaf30ab2..b0cac7d405 100644 --- a/Telegram/SourceFiles/dropdown.h +++ b/Telegram/SourceFiles/dropdown.h @@ -344,7 +344,7 @@ public: void refreshStickers(); void refreshRecentStickers(bool resize = true); void refreshSavedGifs(); - void refreshInlineRows(UserData *bot, const InlineResults &results, bool resultsDeleted); + int32 refreshInlineRows(UserData *bot, const InlineResults &results, bool resultsDeleted); void refreshRecent(); void inlineBotChanged(); void hideInlineRowsPanel(); @@ -458,7 +458,7 @@ private: InlineLayouts _inlineLayouts; LayoutInlineItem *layoutPrepareInlineResult(InlineResult *result, int32 position); - void inlineRowsAddItem(DocumentData *savedGif, InlineResult *result, InlineRow &row, int32 &sumWidth); + bool inlineRowsAddItem(DocumentData *savedGif, InlineResult *result, InlineRow &row, int32 &sumWidth); bool inlineRowFinalize(InlineRow &row, int32 &sumWidth, bool force = false); InlineRow &layoutInlineRow(InlineRow &row, int32 sumWidth = 0); @@ -712,10 +712,10 @@ private: QTimer _inlineRequestTimer; void inlineBotChanged(); - void showInlineRows(bool newResults); + int32 showInlineRows(bool newResults); bool hideOnNoInlineResults(); void recountContentMaxHeight(); - bool refreshInlineRows(); + bool refreshInlineRows(int32 *added = 0); UserData *_inlineBot; QString _inlineQuery, _inlineNextQuery, _inlineNextOffset; mtpRequestId _inlineRequestId; diff --git a/Telegram/SourceFiles/gui/animation.cpp b/Telegram/SourceFiles/gui/animation.cpp index e373f58a44..ee49bb92c1 100644 --- a/Telegram/SourceFiles/gui/animation.cpp +++ b/Telegram/SourceFiles/gui/animation.cpp @@ -225,7 +225,7 @@ ClipReader::ClipReader(const FileLocation &location, const QByteArray &data, Cal , _state(ClipReading) , _width(0) , _height(0) -, _step(FirstFrameNotReadStep) +, _step(WaitingForDimensionsStep) , _paused(0) , _autoplay(false) , _private(0) { @@ -248,47 +248,69 @@ ClipReader::ClipReader(const FileLocation &location, const QByteArray &data, Cal _clipManagers.at(_threadIndex)->append(this, location, data); } -ClipReader::Frame *ClipReader::frameToShow() const { // 0 means not ready - int32 step = _step.loadAcquire(); - if (step == FirstFrameNotReadStep) { - return 0; - } else if (step == WaitingForRequestStep) { - return _frames; - } - return _frames + (((step + 1) / 2) % 3); -} - -ClipReader::Frame *ClipReader::frameToWrite(int32 *index) const { // 0 means not ready - int32 step = _step.loadAcquire(), i = 0; - if (step == WaitingForRequestStep) { +ClipReader::Frame *ClipReader::frameToShow(int32 *index) const { // 0 means not ready + int32 step = _step.loadAcquire(), i; + if (step == WaitingForDimensionsStep) { if (index) *index = 0; return 0; - } else if (step != FirstFrameNotReadStep) { - i = (((step + 3) / 2) % 3); + } else if (step == WaitingForRequestStep) { + i = 0; + } else if (step == WaitingForFirstFrameStep) { + i = 0; + } else { + i = (step / 2) % 3; } if (index) *index = i; return _frames + i; } -ClipReader::Frame *ClipReader::frameToWriteNext(bool checkNotWriting) const { - int32 step = _step.loadAcquire(); - if (step == FirstFrameNotReadStep || step == WaitingForRequestStep || (checkNotWriting && !(step % 2))) { +ClipReader::Frame *ClipReader::frameToWrite(int32 *index) const { // 0 means not ready + int32 step = _step.loadAcquire(), i; + if (step == WaitingForDimensionsStep) { + i = 0; + } else if (step == WaitingForRequestStep) { + if (index) *index = 0; + return 0; + } else if (step == WaitingForFirstFrameStep) { + i = 0; + } else { + i = ((step + 2) / 2) % 3; + } + if (index) *index = i; + return _frames + i; +} + +ClipReader::Frame *ClipReader::frameToWriteNext(bool checkNotWriting, int32 *index) const { + int32 step = _step.loadAcquire(), i; + if (step == WaitingForDimensionsStep || step == WaitingForRequestStep || (checkNotWriting && (step % 2))) { + if (index) *index = 0; return 0; } - return _frames + (((step + 5) / 2) % 3); + i = ((step + 4) / 2) % 3; + if (index) *index = i; + return _frames + i; } void ClipReader::moveToNextShow() const { int32 step = _step.loadAcquire(); - if (step % 2) { - _step.storeRelease((step + 1) % 6); + if (step == WaitingForDimensionsStep) { + } else if (step == WaitingForRequestStep) { + _step.storeRelease(WaitingForFirstFrameStep); + } else if (step == WaitingForFirstFrameStep) { + } else if (!(step % 2)) { + _step.storeRelease(step + 1); } } void ClipReader::moveToNextWrite() const { int32 step = _step.loadAcquire(); - if (!(step % 2)) { - _step.storeRelease(step + 1); + if (step == WaitingForDimensionsStep) { + _step.storeRelease(WaitingForRequestStep); + } else if (step == WaitingForRequestStep) { + } else if (step == WaitingForFirstFrameStep) { + _step.storeRelease(0); + } else if (step % 2) { + _step.storeRelease((step + 1) % 6); } } @@ -313,7 +335,7 @@ void ClipReader::start(int32 framew, int32 frameh, int32 outerw, int32 outerh, b request.outerh = outerh * factor; request.rounded = rounded; _frames[0].request = _frames[1].request = _frames[2].request = request; - _step.storeRelease(0); // start working + moveToNextShow(); _clipManagers.at(_threadIndex)->start(this); } } @@ -322,9 +344,8 @@ QPixmap ClipReader::current(int32 framew, int32 frameh, int32 outerw, int32 oute Frame *frame = frameToShow(); t_assert(frame != 0); - frame->displayed = true; if (ms) { - frame->when = ms; + frame->displayed.storeRelease(1); if (_paused.loadAcquire()) { _paused.storeRelease(0); if (_clipManagers.size() <= _threadIndex) error(); @@ -332,6 +353,8 @@ QPixmap ClipReader::current(int32 framew, int32 frameh, int32 outerw, int32 oute _clipManagers.at(_threadIndex)->update(this); } } + } else { + frame->displayed.storeRelease(-1); // displayed, but should be paused } int32 factor(cIntRetinaFactor()); @@ -412,7 +435,8 @@ public: , _device(0) , _dataSize(0) { } - virtual bool readNextFrame(QImage &to, bool &hasAlpha, const QSize &size) = 0; + virtual bool readNextFrame() = 0; + virtual bool renderFrame(QImage &to, bool &hasAlpha, const QSize &size) = 0; virtual int32 nextFrameDelay() = 0; virtual bool start(bool onlyGifv) = 0; virtual ~ClipReaderImplementation() { @@ -459,30 +483,35 @@ public: return false; } - QImage frame; // QGifHandler always reads first to internal QImage and returns it - if (!_reader->read(&frame)) { + _frame = QImage(); // QGifHandler always reads first to internal QImage and returns it + if (!_reader->read(&_frame) || _frame.isNull()) { return false; } --_framesLeft; + return true; + } - if (size.isEmpty() || size == frame.size()) { - int32 w = frame.width(), h = frame.height(); - if (to.width() == w && to.height() == h && to.format() == frame.format()) { - if (to.byteCount() != frame.byteCount()) { - int bpl = qMin(to.bytesPerLine(), frame.bytesPerLine()); + bool renderFrame(QImage &to, bool &hasAlpha, const QSize &size) { + t_assert(!_frame.isNull()); + if (size.isEmpty() || size == _frame.size()) { + int32 w = _frame.width(), h = _frame.height(); + if (to.width() == w && to.height() == h && to.format() == _frame.format()) { + if (to.byteCount() != _frame.byteCount()) { + int bpl = qMin(to.bytesPerLine(), _frame.bytesPerLine()); for (int i = 0; i < h; ++i) { - memcpy(to.scanLine(i), frame.constScanLine(i), bpl); + memcpy(to.scanLine(i), _frame.constScanLine(i), bpl); } } else { - memcpy(to.bits(), frame.constBits(), frame.byteCount()); + memcpy(to.bits(), _frame.constBits(), _frame.byteCount()); } } else { - to = frame.copy(); + to = _frame.copy(); } } else { - to = frame.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + to = _frame.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } - hasAlpha = frame.hasAlphaChannel(); + hasAlpha = _frame.hasAlphaChannel(); + _frame = QImage(); return true; } @@ -502,6 +531,7 @@ public: private: QImageReader *_reader; int32 _framesLeft, _frameDelay; + QImage _frame; bool jumpToStart() { if (_reader && _reader->jumpToImage(0)) { @@ -540,6 +570,7 @@ public: , _frame(0) , _opened(false) , _hadFrame(false) + , _frameRead(false) , _packetSize(0) , _packetData(0) , _packetWas(false) @@ -555,7 +586,7 @@ public: _avpkt.size = 0; } - bool readNextFrame(QImage &to, bool &hasAlpha, const QSize &size) { + bool readNextFrame() { int res; while (true) { if (_avpkt.size > 0) { // previous packet not finished @@ -607,56 +638,7 @@ public: } if (got_frame) { - _hadFrame = true; - - if (!_width || !_height) { - _width = _frame->width; - _height = _frame->height; - if (!_width || !_height) { - LOG(("Gif Error: Bad frame size %1").arg(logData())); - return false; - } - } - - QSize toSize(size.isEmpty() ? QSize(_width, _height) : size); - if (to.isNull() || to.size() != toSize) { - to = QImage(toSize, QImage::Format_ARGB32); - } - hasAlpha = (_frame->format == AV_PIX_FMT_BGRA || (_frame->format == -1 && _codecContext->pix_fmt == AV_PIX_FMT_BGRA)); - if (_frame->width == toSize.width() && _frame->height == toSize.height() && hasAlpha) { - int32 sbpl = _frame->linesize[0], dbpl = to.bytesPerLine(), bpl = qMin(sbpl, dbpl); - uchar *s = _frame->data[0], *d = to.bits(); - for (int32 i = 0, l = _frame->height; i < l; ++i) { - memcpy(d + i * dbpl, s + i * sbpl, bpl); - } - } else { - if ((_swsSize != toSize) || (_frame->format != -1 && _frame->format != _codecContext->pix_fmt) || !_swsContext) { - _swsSize = toSize; - _swsContext = sws_getCachedContext(_swsContext, _frame->width, _frame->height, AVPixelFormat(_frame->format), toSize.width(), toSize.height(), AV_PIX_FMT_BGRA, 0, 0, 0, 0); - } - uint8_t * toData[1] = { to.bits() }; - int toLinesize[1] = { to.bytesPerLine() }; - if ((res = sws_scale(_swsContext, _frame->data, _frame->linesize, 0, _frame->height, toData, toLinesize)) != _swsSize.height()) { - LOG(("Gif Error: Unable to sws_scale to good size %1, height %2, should be %3").arg(logData()).arg(res).arg(_swsSize.height())); - return false; - } - } - - int64 duration = av_frame_get_pkt_duration(_frame); - int64 framePts = (_frame->pkt_pts == AV_NOPTS_VALUE) ? _frame->pkt_dts : _frame->pkt_pts; - int64 frameMs = (framePts * 1000LL * _fmtContext->streams[_streamId]->time_base.num) / _fmtContext->streams[_streamId]->time_base.den; - _currentFrameDelay = _nextFrameDelay; - if (_frameMs + _currentFrameDelay < frameMs) { - _currentFrameDelay = int32(frameMs - _frameMs); - } - if (duration == AV_NOPTS_VALUE) { - _nextFrameDelay = 0; - } else { - _nextFrameDelay = (duration * 1000LL * _fmtContext->streams[_streamId]->time_base.num) / _fmtContext->streams[_streamId]->time_base.den; - } - _frameMs = frameMs; - - av_frame_unref(_frame); + _hadFrame = _frameRead = true; return true; } @@ -681,6 +663,61 @@ public: return false; } + bool renderFrame(QImage &to, bool &hasAlpha, const QSize &size) { + t_assert(_frameRead); + _frameRead = false; + + if (!_width || !_height) { + _width = _frame->width; + _height = _frame->height; + if (!_width || !_height) { + LOG(("Gif Error: Bad frame size %1").arg(logData())); + return false; + } + } + + QSize toSize(size.isEmpty() ? QSize(_width, _height) : size); + if (to.isNull() || to.size() != toSize) { + to = QImage(toSize, QImage::Format_ARGB32); + } + hasAlpha = (_frame->format == AV_PIX_FMT_BGRA || (_frame->format == -1 && _codecContext->pix_fmt == AV_PIX_FMT_BGRA)); + if (_frame->width == toSize.width() && _frame->height == toSize.height() && hasAlpha) { + int32 sbpl = _frame->linesize[0], dbpl = to.bytesPerLine(), bpl = qMin(sbpl, dbpl); + uchar *s = _frame->data[0], *d = to.bits(); + for (int32 i = 0, l = _frame->height; i < l; ++i) { + memcpy(d + i * dbpl, s + i * sbpl, bpl); + } + } else { + if ((_swsSize != toSize) || (_frame->format != -1 && _frame->format != _codecContext->pix_fmt) || !_swsContext) { + _swsSize = toSize; + _swsContext = sws_getCachedContext(_swsContext, _frame->width, _frame->height, AVPixelFormat(_frame->format), toSize.width(), toSize.height(), AV_PIX_FMT_BGRA, 0, 0, 0, 0); + } + uint8_t * toData[1] = { to.bits() }; + int toLinesize[1] = { to.bytesPerLine() }, res; + if ((res = sws_scale(_swsContext, _frame->data, _frame->linesize, 0, _frame->height, toData, toLinesize)) != _swsSize.height()) { + LOG(("Gif Error: Unable to sws_scale to good size %1, height %2, should be %3").arg(logData()).arg(res).arg(_swsSize.height())); + return false; + } + } + + int64 duration = av_frame_get_pkt_duration(_frame); + int64 framePts = (_frame->pkt_pts == AV_NOPTS_VALUE) ? _frame->pkt_dts : _frame->pkt_pts; + int64 frameMs = (framePts * 1000LL * _fmtContext->streams[_streamId]->time_base.num) / _fmtContext->streams[_streamId]->time_base.den; + _currentFrameDelay = _nextFrameDelay; + if (_frameMs + _currentFrameDelay < frameMs) { + _currentFrameDelay = int32(frameMs - _frameMs); + } + if (duration == AV_NOPTS_VALUE) { + _nextFrameDelay = 0; + } else { + _nextFrameDelay = (duration * 1000LL * _fmtContext->streams[_streamId]->time_base.num) / _fmtContext->streams[_streamId]->time_base.den; + } + _frameMs = frameMs; + + av_frame_unref(_frame); + return true; + } + int32 nextFrameDelay() { return _currentFrameDelay; } @@ -776,7 +813,7 @@ private: AVCodecContext *_codecContext; int32 _streamId; AVFrame *_frame; - bool _opened, _hadFrame; + bool _opened, _hadFrame, _frameRead; AVPacket _avpkt; int _packetSize; @@ -835,9 +872,7 @@ public: , _frame(0) , _width(0) , _height(0) - , _previousMs(0) - , _currentMs(0) - , _nextUpdateMs(0) + , _nextFrameWhen(0) , _paused(false) { if (_data.isEmpty() && !_location->accessEnable()) { error(); @@ -851,7 +886,10 @@ public: return error(); } if (frame() && frame()->original.isNull()) { - if (!_implementation->readNextFrame(frame()->original, frame()->alpha, QSize())) { + if (!_implementation->readNextFrame()) { + return error(); + } + if (!_implementation->renderFrame(frame()->original, frame()->alpha, QSize())) { return error(); } _width = frame()->original.width(); @@ -868,23 +906,22 @@ public: return start(ms); } - if (!_paused && ms >= _nextUpdateMs) { + if (!_paused && ms >= _nextFrameWhen) { return ClipProcessRepaint; } return ClipProcessWait; } ClipProcessResult finishProcess(uint64 ms) { - _previousMs = _currentMs; - if (!prepareNextFrame()) { + if (!readNextFrame()) { return error(); } - if (ms >= _nextUpdateMs) { - if (!prepareNextFrame()) { - return error(); - } + if (ms >= _nextFrameWhen && !readNextFrame(true)) { + return error(); + } + if (!renderFrame()) { + return error(); } - _currentMs = qMax(ms, _nextUpdateMs); return ClipProcessCopyFrame; } @@ -893,15 +930,26 @@ public: return qMax(delay, 5); } - bool prepareNextFrame() { - t_assert(frame() != 0 && _request.valid()); - if (!_implementation->readNextFrame(frame()->original, frame()->alpha, QSize(_request.framew, _request.frameh))) { + bool readNextFrame(bool keepup = false) { + if (!_implementation->readNextFrame()) { + return false; + } + _nextFrameWhen += nextFrameDelay(); + if (keepup) { + _nextFrameWhen = qMax(_nextFrameWhen, getms()); + } + return true; + } + + bool renderFrame() { + t_assert(frame() != 0 && _request.valid()); + if (!_implementation->renderFrame(frame()->original, frame()->alpha, QSize(_request.framew, _request.frameh))) { return false; } - _nextUpdateMs = _currentMs + nextFrameDelay(); frame()->original.setDevicePixelRatio(_request.factor); frame()->pix = QPixmap(); frame()->pix = _prepareFrame(_request, frame()->original, frame()->alpha, frame()->cache); + frame()->when = _nextFrameWhen; return true; } @@ -962,11 +1010,12 @@ private: ClipFrameRequest _request; struct Frame { - Frame() : alpha(true) { + Frame() : alpha(true), when(0) { } QPixmap pix; QImage original, cache; bool alpha; + uint64 when; }; Frame _frames[3]; int32 _frame; @@ -976,7 +1025,7 @@ private: int32 _width, _height; - uint64 _previousMs, _currentMs, _nextUpdateMs; + uint64 _nextFrameWhen; bool _paused; @@ -1070,13 +1119,16 @@ bool ClipReadManager::handleProcessResult(ClipReaderPrivate *reader, ClipProcess if (result == ClipProcessStarted) { _loadLevel.fetchAndAddRelaxed(reader->_width * reader->_height - AverageGifSize); } - if (!reader->_paused && (result == ClipProcessRepaint || result == ClipProcessWait)) { - ClipReader::Frame *other = it.key()->frameToWriteNext(false); - t_assert(other != 0); - if (other->when && other->when + WaitBeforeGifPause < qMax(reader->_previousMs, ms)) { - reader->_paused = true; - it.key()->_paused.storeRelease(1); - result = ClipProcessPaused; + if (!reader->_paused && result == ClipProcessRepaint) { + int32 ishowing, iprevious; + ClipReader::Frame *showing = it.key()->frameToShow(&ishowing), *previous = it.key()->frameToWriteNext(false, &iprevious); + t_assert(previous != 0 && showing != 0 && ishowing >= 0 && iprevious >= 0); + if (reader->_frames[ishowing].when > 0 && showing->displayed.loadAcquire() <= 0) { // current frame was not shown + if (reader->_frames[ishowing].when + WaitBeforeGifPause < ms || (reader->_frames[iprevious].when && previous->displayed.loadAcquire() <= 0)) { + reader->_paused = true; + it.key()->_paused.storeRelease(1); + result = ClipProcessPaused; + } } } if (result == ClipProcessStarted || result == ClipProcessCopyFrame) { @@ -1085,14 +1137,17 @@ bool ClipReadManager::handleProcessResult(ClipReaderPrivate *reader, ClipProcess frame->clear(); frame->pix = reader->frame()->pix; frame->original = reader->frame()->original; - frame->displayed = false; - it.key()->moveToNextWrite(); + frame->displayed.storeRelease(0); if (result == ClipProcessStarted) { + reader->_nextFrameWhen = ms; + it.key()->moveToNextWrite(); emit callback(it.key(), it.key()->threadIndex(), ClipReaderReinit); } } else if (result == ClipProcessPaused) { + it.key()->moveToNextWrite(); emit callback(it.key(), it.key()->threadIndex(), ClipReaderReinit); } else if (result == ClipProcessRepaint) { + it.key()->moveToNextWrite(); emit callback(it.key(), it.key()->threadIndex(), ClipReaderRepaint); } return true; @@ -1174,7 +1229,7 @@ void ClipReadManager::process() { return; } ms = getms(); - i.value() = reader->_nextUpdateMs ? reader->_nextUpdateMs : (ms + 86400 * 1000ULL); + i.value() = reader->_nextFrameWhen ? reader->_nextFrameWhen : (ms + 86400 * 1000ULL); } if (!reader->_paused && i.value() < minms) { minms = i.value(); @@ -1224,7 +1279,7 @@ MTPDocumentAttribute clipReadAnimatedAttributes(const QString &fname, const QByt FFMpegReaderImplementation *reader = new FFMpegReaderImplementation(&localloc, &localdata); if (reader->start(true)) { bool hasAlpha = false; - if (reader->readNextFrame(cover, hasAlpha, QSize())) { + if (reader->readNextFrame() && reader->renderFrame(cover, hasAlpha, QSize())) { if (cover.width() > 0 && cover.height() > 0 && cover.width() < cover.height() * 10 && cover.height() < cover.width() * 10) { if (hasAlpha) { QImage cacheForResize; diff --git a/Telegram/SourceFiles/gui/animation.h b/Telegram/SourceFiles/gui/animation.h index d6edea257f..b3aa52afa0 100644 --- a/Telegram/SourceFiles/gui/animation.h +++ b/Telegram/SourceFiles/gui/animation.h @@ -497,8 +497,9 @@ enum ClipReaderNotification { }; enum ClipReaderSteps { - FirstFrameNotReadStep = -2, - WaitingForRequestStep = -1, + WaitingForDimensionsStep = -3, // before ClipReaderPrivate read the first image and got the original frame size + WaitingForRequestStep = -2, // before ClipReader got the original frame size and prepared the frame request + WaitingForFirstFrameStep = -1, // before ClipReaderPrivate got the frame request and started waiting for the 1-2 delay }; class ClipReaderPrivate; @@ -528,7 +529,7 @@ public: } bool currentDisplayed() const { Frame *frame = frameToShow(); - return frame ? frame->displayed : true; + return frame ? (frame->displayed.loadAcquire() != 0) : true; } bool paused() const { return _paused.loadAcquire(); @@ -542,7 +543,8 @@ public: ClipState state() const; bool started() const { - return _step.loadAcquire() >= 0; + int32 step = _step.loadAcquire(); + return (step == WaitingForFirstFrameStep) || (step >= 0); } bool ready() const; @@ -561,7 +563,7 @@ private: mutable QAtomicInt _step; // -2, -1 - init, 0-5 - work, show ((state + 1) / 2) % 3 state, write ((state + 3) / 2) % 3 struct Frame { - Frame() : displayed(false), when(0) { + Frame() : displayed(false) { } void clear() { pix = QPixmap(); @@ -570,13 +572,12 @@ private: QPixmap pix; QImage original; ClipFrameRequest request; - bool displayed; - uint64 when; + QAtomicInt displayed; }; mutable Frame _frames[3]; - Frame *frameToShow() const; // 0 means not ready + Frame *frameToShow(int32 *index = 0) const; // 0 means not ready Frame *frameToWrite(int32 *index = 0) const; // 0 means not ready - Frame *frameToWriteNext(bool check) const; + Frame *frameToWriteNext(bool check, int32 *index = 0) const; void moveToNextShow() const; void moveToNextWrite() const; diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 4f4415bd8c..2096bd2474 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -1392,7 +1392,9 @@ HistoryItem *History::createItem(HistoryBlock *block, const MTPMessage &msg, boo case mtpc_messageMediaUnsupported: default: badMedia = 1; break; } - if (badMedia) { + if (false && badMedia == 1) { +// QString text(lng_message_unsupported(lt_link, qsl("https://desktop.telegram.org"))); + } else if (badMedia) { result = new HistoryServiceMsg(this, block, m.vid.v, date(m.vdate), lang((badMedia == 2) ? lng_message_empty : lng_media_unsupported), m.vflags.v, 0, m.has_from_id() ? m.vfrom_id.v : 0); } else { if ((m.has_fwd_date() && m.vfwd_date.v > 0) || (m.has_fwd_from_id() && peerFromMTP(m.vfwd_from_id) != 0)) { @@ -3352,11 +3354,16 @@ void HistoryPhoto::draw(Painter &p, const HistoryItem *parent, const QRect &r, b p.setOpacity(radialOpacity); style::sprite icon; if (radial || _data->loading()) { - icon = (selected ? st::msgFileInCancelSelected : st::msgFileInCancel); + DelayedStorageImage *delayed = _data->full->toDelayedStorageImage(); + if (!delayed || !delayed->location().isNull()) { + icon = (selected ? st::msgFileInCancelSelected : st::msgFileInCancel); + } } else { icon = (selected ? st::msgFileInDownloadSelected : st::msgFileInDownload); } - p.drawSpriteCenter(inner, icon); + if (!icon.isEmpty()) { + p.drawSpriteCenter(inner, icon); + } if (radial) { p.setOpacity(1); QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine))); @@ -3401,8 +3408,15 @@ void HistoryPhoto::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x if (x >= skipx && y >= skipy && x < skipx + width && y < skipy + height) { if (_data->uploading()) { lnk = _cancell; + } else if (_data->loaded()) { + lnk = _openl; + } else if (_data->loading()) { + DelayedStorageImage *delayed = _data->full->toDelayedStorageImage(); + if (!delayed || !delayed->location().isNull()) { + lnk = _cancell; + } } else { - lnk = _data->loaded() ? _openl : (_data->loading() ? _cancell : _savel); + lnk = _savel; } if (_caption.isEmpty() && parent->getMedia() == this) { int32 fullRight = skipx + width, fullBottom = skipy + height; @@ -4561,11 +4575,15 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo if (_data->loaded() && !radial) { icon = (selected ? st::msgFileInPlaySelected : st::msgFileInPlay); } else if (radial || _data->loading()) { - icon = (selected ? st::msgFileInCancelSelected : st::msgFileInCancel); + if (parent->id > 0) { + icon = (selected ? st::msgFileInCancelSelected : st::msgFileInCancel); + } } else { icon = (selected ? st::msgFileInDownloadSelected : st::msgFileInDownload); } - p.drawSpriteCenter(inner, icon); + if (!icon.isEmpty()) { + p.drawSpriteCenter(inner, icon); + } if (radial) { p.setOpacity(1); QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine))); @@ -4762,6 +4780,16 @@ void HistorySticker::initDimensions(const HistoryItem *parent) { _height = _minh; } +int32 HistorySticker::resize(int32 width, const HistoryItem *parent) { // return new height + _width = qMin(width, _maxw); + if (const HistoryReply *reply = toHistoryReply(parent)) { + int32 usew = _maxw - st::msgReplyPadding.left() - reply->replyToWidth(); + int32 rw = _width - usew - st::msgReplyPadding.left() - st::msgReplyPadding.left() - st::msgReplyPadding.right(); + reply->resizeVia(rw); + } + return _height; +} + void HistorySticker::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return; @@ -5958,7 +5986,7 @@ void ViaInlineBotLink::onClick(Qt::MouseButton button) const { HistoryMessageVia::HistoryMessageVia(int32 userId) : bot(App::userLoaded(peerFromUser(userId))) , width(0) - , fullWidth(st::msgServiceNameFont->width(qsl("via @") + bot->username)) + , maxWidth(st::msgServiceNameFont->width(qsl("via @") + bot->username)) , lnk(new ViaInlineBotLink(bot)) { } @@ -5967,13 +5995,21 @@ bool HistoryMessageVia::isNull() const { } void HistoryMessageVia::resize(int32 availw) { - if (width < fullWidth && availw > width) { - if (availw < fullWidth) { + if (width < maxWidth && availw > width) { + if (availw < maxWidth) { text = st::msgServiceNameFont->elided(qsl("via @") + bot->username, availw); width = st::msgServiceNameFont->width(text); } else { text = qsl("via @") + bot->username; - width = fullWidth; + width = maxWidth; + } + } else if (availw < width) { + if (availw > 0) { + text = st::msgServiceNameFont->elided(qsl("via @") + bot->username, availw); + width = st::msgServiceNameFont->width(text); + } else { + text = QString(); + width = 0; } } } @@ -6165,9 +6201,9 @@ void HistoryMessage::initDimensions() { if (maxw > _maxw) _maxw = maxw; _minh += _media->minHeight(); } - if (via()) { - if (st::msgPadding.left() + via()->fullWidth + st::msgPadding.right() > _maxw) { - _maxw = st::msgPadding.left() + via()->fullWidth + st::msgPadding.right() > _maxw; + if (!_media && !displayFromName() && via() && !toHistoryForwarded()) { + if (st::msgPadding.left() + via()->maxWidth + st::msgPadding.right() > _maxw) { + _maxw = st::msgPadding.left() + via()->maxWidth + st::msgPadding.right(); } } } else { @@ -6200,8 +6236,11 @@ void HistoryMessage::countPositionAndSize(int32 &left, int32 &width) const { void HistoryMessage::fromNameUpdated() const { if (!_media && displayFromName()) { - int32 _namew = st::msgPadding.left() + _from->nameText.maxWidth() + st::msgPadding.right(); - if (_namew > _maxw) _maxw = _namew; + int32 namew = st::msgPadding.left() + _from->nameText.maxWidth() + st::msgPadding.right(); + if (via() && !toHistoryForwarded()) { + namew += st::msgServiceFont->spacew + via()->maxWidth; + } + if (namew > _maxw) _maxw = namew; } } @@ -6437,7 +6476,7 @@ void HistoryMessage::draw(Painter &p, const QRect &r, uint32 selection, uint64 m } if (_from->nameVersion > _fromVersion) { - fromNameUpdated(); +// fromNameUpdated(); _fromVersion = _from->nameVersion; } @@ -6464,6 +6503,10 @@ void HistoryMessage::draw(Painter &p, const QRect &r, uint32 selection, uint64 m p.setPen(_from->color); } _from->nameText.drawElided(p, r.left() + st::msgPadding.left(), r.top() + st::msgPadding.top(), width - st::msgPadding.left() - st::msgPadding.right()); + if (via() && !toHistoryForwarded() && width > st::msgPadding.left() + st::msgPadding.right() + _from->nameText.maxWidth() + st::msgServiceFont->spacew) { + p.setPen(selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg)); + p.drawText(r.left() + st::msgPadding.left() + _from->nameText.maxWidth() + st::msgServiceFont->spacew, r.top() + st::msgPadding.top() + st::msgServiceFont->ascent, via()->text); + } r.setTop(r.top() + st::msgNameFont->height); } @@ -6495,15 +6538,15 @@ void HistoryMessage::draw(Painter &p, const QRect &r, uint32 selection, uint64 m void HistoryMessage::drawMessageText(Painter &p, QRect trect, uint32 selection) const { bool outbg = out() && !fromChannel(), selected = (selection == FullSelection); - if (via()) { + if (!displayFromName() && via() && !toHistoryForwarded()) { p.setFont(st::msgServiceNameFont); - p.setPen((selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg))->p); + p.setPen(selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg)); p.drawTextLeft(trect.left(), trect.top(), _history->width, via()->text); trect.setY(trect.y() + st::msgServiceNameFont->height); } - p.setPen(st::msgColor->p); - p.setFont(st::msgFont->f); + p.setPen(st::msgColor); + p.setFont(st::msgFont); uint16 selectedFrom = (selection == FullSelection) ? 0 : (selection >> 16) & 0xFFFF; uint16 selectedTo = (selection == FullSelection) ? 0 : selection & 0xFFFF; _text.draw(p, trect.x(), trect.y(), trect.width(), Qt::AlignLeft, 0, -1, selectedFrom, selectedTo); @@ -6549,8 +6592,10 @@ int32 HistoryMessage::resize(int32 width) { } else { _height += st::msgNameFont->height; } - } - if (via()) { + if (via() && !toHistoryForwarded()) { + via()->resize(width - st::msgPadding.left() - st::msgPadding.right() - _from->nameText.maxWidth() - st::msgServiceFont->spacew); + } + } else if (via() && !toHistoryForwarded()) { via()->resize(width - st::msgPadding.left() - st::msgPadding.right()); if (emptyText() && !displayFromName()) { _height += st::msgPadding.top() + st::msgNameFont->height + st::mediaHeaderSkip; @@ -6612,9 +6657,15 @@ void HistoryMessage::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 if (drawBubble()) { QRect r(left, st::msgMargin.top(), width, _height - st::msgMargin.top() - st::msgMargin.bottom()); if (displayFromName()) { // from user left name - if (x >= r.left() + st::msgPadding.left() && y >= r.top() + st::msgPadding.top() && y < r.top() + st::msgPadding.top() + st::msgNameFont->height && x < r.left() + r.width() - st::msgPadding.right() && x < r.left() + st::msgPadding.left() + _from->nameText.maxWidth()) { - lnk = _from->lnk; - return; + if (y >= r.top() + st::msgPadding.top() && y < r.top() + st::msgPadding.top() + st::msgNameFont->height) { + if (x >= r.left() + st::msgPadding.left() && x < r.left() + r.width() - st::msgPadding.right() && x < r.left() + st::msgPadding.left() + _from->nameText.maxWidth()) { + lnk = _from->lnk; + return; + } + if (via() && !toHistoryForwarded() && x >= r.left() + st::msgPadding.left() + _from->nameText.maxWidth() + st::msgServiceFont->spacew && x < r.left() + st::msgPadding.left() + _from->nameText.maxWidth() + st::msgServiceFont->spacew + via()->width) { + lnk = via()->lnk; + return; + } } r.setTop(r.top() + st::msgNameFont->height); } @@ -6629,7 +6680,7 @@ void HistoryMessage::getStateFromMessageText(TextLinkPtr &lnk, HistoryCursorStat QRect trect(r.marginsAdded(-st::msgPadding)); - if (via()) { + if (!displayFromName() && via() && !toHistoryForwarded()) { if (x >= trect.left() && y >= trect.top() && y < trect.top() + st::msgNameFont->height && x < trect.left() + via()->width) { lnk = via()->lnk; return; @@ -6678,8 +6729,7 @@ void HistoryMessage::getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, QRect r(left, st::msgMargin.top(), width, _height - st::msgMargin.top() - st::msgMargin.bottom()); if (displayFromName()) { // from user left name r.setTop(r.top() + st::msgNameFont->height); - } - if (via()) { + } else if (via() && !toHistoryForwarded()) { r.setTop(r.top() + st::msgNameFont->height); } QRect trect(r.marginsAdded(-st::msgPadding)); @@ -6765,12 +6815,16 @@ void HistoryForwarded::initDimensions() { HistoryMessage::initDimensions(); if (!_media) { int32 _namew = st::msgPadding.left() + fromWidth + fwdFromName.maxWidth() + st::msgPadding.right(); + if (via()) { + _namew += st::msgServiceFont->spacew + via()->maxWidth; + } if (_namew > _maxw) _maxw = _namew; } } void HistoryForwarded::fwdNameUpdated() const { - fwdFromName.setText(st::msgServiceNameFont, App::peerName(fwdFrom), _textNameOptions); + QString fwdName((via() && fwdFrom->isUser()) ? fwdFrom->asUser()->firstName : App::peerName(fwdFrom)); + fwdFromName.setText(st::msgServiceNameFont, fwdName, _textNameOptions); } void HistoryForwarded::draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const { @@ -6788,10 +6842,17 @@ void HistoryForwarded::drawForwardedFrom(Painter &p, int32 x, int32 y, int32 w, p.setPen((selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg))->p); p.setFont(serviceFont); - if (w >= fromWidth) { + if (via() && w > fromWidth + fwdFromName.maxWidth() + serviceFont->spacew) { p.drawText(x, y + serviceFont->ascent, lang(lng_forwarded_from)); - p.setFont(serviceName->f); + p.setFont(serviceName); + fwdFromName.draw(p, x + fromWidth, y, w - fromWidth); + + p.drawText(x + fromWidth + fwdFromName.maxWidth() + serviceFont->spacew, y + serviceFont->ascent, via()->text); + } else if (w > fromWidth) { + p.drawText(x, y + serviceFont->ascent, lang(lng_forwarded_from)); + + p.setFont(serviceName); fwdFromName.drawElided(p, x + fromWidth, y, w - fromWidth); } else { p.drawText(x, y + serviceFont->ascent, serviceFont->elided(lang(lng_forwarded_from), w)); @@ -6810,11 +6871,14 @@ int32 HistoryForwarded::resize(int32 width) { HistoryMessage::resize(width); if (drawBubble()) { if (displayForwardedFrom()) { - if (emptyText() && !displayFromName() && !via()) { + if (emptyText() && !displayFromName()) { _height += st::msgPadding.top() + st::msgServiceNameFont->height + st::mediaHeaderSkip; } else { _height += st::msgServiceNameFont->height; } + if (via()) { + via()->resize(width - st::msgPadding.left() - st::msgPadding.right() - fromWidth - fwdFromName.maxWidth() - st::msgServiceFont->spacew); + } } } return _height; @@ -6876,6 +6940,8 @@ void HistoryForwarded::getForwardedState(TextLinkPtr &lnk, HistoryCursorState &s state = HistoryDefaultCursorState; if (x >= fromWidth && x < w && x < fromWidth + fwdFromName.maxWidth()) { lnk = fwdFrom->lnk; + } else if (via() && x >= fromWidth + fwdFromName.maxWidth() + st::msgServiceFont->spacew && x < w && x < fromWidth + fwdFromName.maxWidth() + st::msgServiceFont->spacew + via()->maxWidth) { + lnk = via()->lnk; } else { lnk = TextLinkPtr(); } @@ -6910,7 +6976,8 @@ HistoryReply::HistoryReply(History *history, HistoryBlock *block, const MTPDmess , replyToMsgId(msg.vreply_to_msg_id.v) , replyToMsg(0) , replyToVersion(0) -, _maxReplyWidth(0) { +, _maxReplyWidth(0) +, _replyToVia(0) { if (!updateReplyTo() && App::api()) { App::api()->requestReplyTo(this, history->peer->asChannel(), replyToMsgId); } @@ -6921,7 +6988,8 @@ HistoryReply::HistoryReply(History *history, HistoryBlock *block, MsgId msgId, i , replyToMsgId(replyTo) , replyToMsg(0) , replyToVersion(0) -, _maxReplyWidth(0) { +, _maxReplyWidth(0) +, _replyToVia(0) { if (!updateReplyTo() && App::api()) { App::api()->requestReplyTo(this, history->peer->asChannel(), replyToMsgId); } @@ -6932,7 +7000,8 @@ HistoryReply::HistoryReply(History *history, HistoryBlock *block, MsgId msgId, i , replyToMsgId(replyTo) , replyToMsg(0) , replyToVersion(0) -, _maxReplyWidth(0) { +, _maxReplyWidth(0) +, _replyToVia(0) { if (!updateReplyTo() && App::api()) { App::api()->requestReplyTo(this, history->peer->asChannel(), replyToMsgId); } @@ -6952,6 +7021,9 @@ void HistoryReply::initDimensions() { HistoryMessage::initDimensions(); if (!_media) { int32 replyw = st::msgPadding.left() + _maxReplyWidth - st::msgReplyPadding.left() - st::msgReplyPadding.right() + st::msgPadding.right(); + if (replyToVia()) { + replyw += st::msgServiceFont->spacew + via()->maxWidth; + } if (replyw > _maxw) _maxw = replyw; } } @@ -6959,6 +7031,7 @@ void HistoryReply::initDimensions() { bool HistoryReply::updateReplyTo(bool force) { if (replyToMsg || !replyToMsgId) return true; replyToMsg = App::histItemById(channelId(), replyToMsgId); + if (replyToMsg) { App::historyRegReply(this, replyToMsg); replyToText.setText(st::msgFont, replyToMsg->inReplyText(), _textDlgOptions); @@ -6966,6 +7039,11 @@ bool HistoryReply::updateReplyTo(bool force) { replyToNameUpdated(); replyToLnk = TextLinkPtr(new MessageLink(replyToMsg->history()->peer->id, replyToMsg->id)); + if (!replyToMsg->toHistoryForwarded()) { + if (UserData *bot = replyToMsg->viaBot()) { + _replyToVia = new HistoryMessageVia(peerToUser(bot->id)); + } + } } else if (force) { replyToMsgId = 0; } @@ -6978,12 +7056,17 @@ bool HistoryReply::updateReplyTo(bool force) { void HistoryReply::replyToNameUpdated() const { if (replyToMsg) { - replyToName.setText(st::msgServiceNameFont, App::peerName(replyToMsg->from()), _textNameOptions); + QString name = (replyToVia() && replyToMsg->from()->isUser()) ? replyToMsg->from()->asUser()->firstName : App::peerName(replyToMsg->from()); + replyToName.setText(st::msgServiceNameFont, name, _textNameOptions); replyToVersion = replyToMsg->from()->nameVersion; bool hasPreview = replyToMsg->getMedia() ? replyToMsg->getMedia()->hasReplyPreview() : false; int32 previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0; + int32 w = replyToName.maxWidth(); + if (replyToVia()) { + w += st::msgServiceFont->spacew + replyToVia()->maxWidth; + } - _maxReplyWidth = previewSkip + qMax(replyToName.maxWidth(), qMin(replyToText.maxWidth(), 4 * replyToName.maxWidth())); + _maxReplyWidth = previewSkip + qMax(w, qMin(replyToText.maxWidth(), 4 * w)); } else { _maxReplyWidth = st::msgDateFont->width(lang(replyToMsgId ? lng_profile_loading : lng_deleted_message)); } @@ -7056,6 +7139,10 @@ void HistoryReply::drawReplyTo(Painter &p, int32 x, int32 y, int32 w, bool selec p.setPen(selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg)); } replyToName.drawLeftElided(p, x + st::msgReplyBarSkip + previewSkip, y + st::msgReplyPadding.top(), w - st::msgReplyBarSkip - previewSkip, w + 2 * x); + if (replyToVia() && w > st::msgReplyBarSkip + previewSkip + replyToName.maxWidth() + st::msgServiceFont->spacew) { + p.setFont(st::msgServiceFont); + p.drawText(x + st::msgReplyBarSkip + previewSkip + replyToName.maxWidth() + st::msgServiceFont->spacew, y + st::msgReplyPadding.top() + st::msgServiceFont->ascent, replyToVia()->text); + } HistoryMessage *replyToAsMsg = replyToMsg->toHistoryMessage(); if (likeService) { @@ -7094,10 +7181,23 @@ int32 HistoryReply::resize(int32 width) { } else { _height += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom(); } + if (replyToVia()) { + bool hasPreview = replyToMsg->getMedia() ? replyToMsg->getMedia()->hasReplyPreview() : false; + int previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0; + replyToVia()->resize(width - st::msgPadding.left() - st::msgPadding.right() - st::msgReplyBarSkip + previewSkip + replyToName.maxWidth() + st::msgServiceFont->spacew); + } } return _height; } +void HistoryReply::resizeVia(int32 w) const { + if (!replyToVia()) return; + + bool hasPreview = replyToMsg->getMedia() ? replyToMsg->getMedia()->hasReplyPreview() : false; + int previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0; + replyToVia()->resize(w - st::msgReplyBarSkip - previewSkip - replyToName.maxWidth() - st::msgServiceFont->spacew); +} + bool HistoryReply::hasPoint(int32 x, int32 y) const { if (drawBubble()) { int32 left = 0, width = 0; @@ -7186,6 +7286,7 @@ HistoryReply::~HistoryReply() { } else if (replyToMsgId && App::api()) { App::api()->itemRemoved(this); } + deleteAndMark(_replyToVia); } void HistoryServiceMsg::setMessageByAction(const MTPmessageAction &action) { diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 326f631767..b6a3821696 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -1680,6 +1680,7 @@ public: } void initDimensions(const HistoryItem *parent); + int32 resize(int32 width, const HistoryItem *parent); void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const; void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const; @@ -1979,7 +1980,7 @@ public: UserData *bot; QString text; - int32 width, fullWidth; + int32 width, maxWidth; TextLinkPtr lnk; }; @@ -2164,7 +2165,7 @@ public: } QString selectedText(uint32 selection) const; bool displayForwardedFrom() const { - return (!_media || !_media->isDisplayed() || !_media->hideForwardedFrom()); + return via() || !_media || !_media->isDisplayed() || !_media->hideForwardedFrom(); } HistoryForwarded *toHistoryForwarded() { @@ -2207,6 +2208,7 @@ public: void drawReplyTo(Painter &p, int32 x, int32 y, int32 w, bool selected, bool likeService = false) const; void drawMessageText(Painter &p, QRect trect, uint32 selection) const; int32 resize(int32 width); + void resizeVia(int32 w) const; bool hasPoint(int32 x, int32 y) const; void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const; void getStateFromMessageText(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const QRect &r) const; @@ -2234,6 +2236,10 @@ protected: mutable Text replyToName, replyToText; mutable int32 replyToVersion; mutable int32 _maxReplyWidth; + HistoryMessageVia *_replyToVia; + HistoryMessageVia *replyToVia() const { + return (_replyToVia && !_replyToVia->isNull()) ? _replyToVia : 0; + } int32 toWidth; }; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index a635b3fa2d..703aa35efb 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -2630,6 +2630,8 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) , _migrated(0) , _history(0) , _histInited(false) +, _lastScroll(0) +, _lastScrolled(0) , _toHistoryEnd(this, st::historyToEnd) , _collapseComments(this) , _attachMention(this) @@ -2722,6 +2724,9 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) connect(audioCapture(), SIGNAL(onDone(QByteArray,qint32)), this, SLOT(onRecordDone(QByteArray,qint32))); } + _updateHistoryItems.setSingleShot(true); + connect(&_updateHistoryItems, SIGNAL(timeout()), this, SLOT(onUpdateHistoryItems())); + _scrollTimer.setSingleShot(false); _sendActionStopTimer.setSingleShot(true); @@ -3508,6 +3513,8 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re _scroll.setWidget(_list); _list->show(); + _updateHistoryItems.stop(); + if (_history->lastWidth || _history->isReadyFor(_showAtMsgId, _fixedInScrollMsgId, _fixedInScrollMsgTop)) { _fixedInScrollMsgId = 0; _fixedInScrollMsgTop = 0; @@ -4360,6 +4367,11 @@ void HistoryWidget::onListScroll() { break; } } + + if (st != _lastScroll) { + _lastScrolled = getms(); + _lastScroll = st; + } } void HistoryWidget::onVisibleChanged() { @@ -4407,8 +4419,9 @@ void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) { _saveDraftStart = getms(); onDraftSave(); - onCheckMentionDropdown(); + if (!_attachMention.isHidden()) _attachMention.hideStart(); if (!_attachType.isHidden()) _attachType.hideStart(); + if (!_emojiPan.isHidden()) _emojiPan.hideStart(); if (replyTo < 0) cancelReply(lastKeyboardUsed); if (_previewData && _previewData->pendingTill) previewCancel(); @@ -5834,7 +5847,23 @@ bool HistoryWidget::isItemVisible(HistoryItem *item) { void HistoryWidget::ui_repaintHistoryItem(const HistoryItem *item) { if (_peer && _list && (item->history() == _history || (_migrated && item->history() == _migrated))) { - _list->repaintItem(item); + uint64 ms = getms(); + if (_lastScrolled + 100 <= ms) { + _list->repaintItem(item); + } else { + _updateHistoryItems.start(_lastScrolled + 100 - ms); + } + } +} + +void HistoryWidget::onUpdateHistoryItems() { + if (!_list) return; + + uint64 ms = getms(); + if (_lastScrolled + 100 <= ms) { + _list->update(); + } else { + _updateHistoryItems.start(_lastScrolled + 100 - ms); } } @@ -6459,8 +6488,9 @@ void HistoryWidget::onInlineResultSend(InlineResult *result, UserData *bot) { Local::writeRecentHashtagsAndBots(); } - onCheckMentionDropdown(); + if (!_attachMention.isHidden()) _attachMention.hideStart(); if (!_attachType.isHidden()) _attachType.hideStart(); + if (!_emojiPan.isHidden()) _emojiPan.hideStart(); _field.setFocus(); } diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 826c84556e..24a9bdad5b 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -682,6 +682,8 @@ public slots: void onRecordDone(QByteArray result, qint32 samples); void onRecordUpdate(qint16 level, qint32 samples); + void onUpdateHistoryItems(); + private: MsgId _replyToId; @@ -771,6 +773,10 @@ private: History *_migrated, *_history; bool _histInited; // initial updateListSize() called + int32 _lastScroll; + uint64 _lastScrolled; + QTimer _updateHistoryItems; // gifs optimization + IconedButton _toHistoryEnd; CollapseButton _collapseComments; diff --git a/Telegram/SourceFiles/layout.cpp b/Telegram/SourceFiles/layout.cpp index e1ac813791..e987cb60e3 100644 --- a/Telegram/SourceFiles/layout.cpp +++ b/Telegram/SourceFiles/layout.cpp @@ -193,6 +193,21 @@ style::color documentColor(int32 colorIndex) { return colors[colorIndex & 3]; } +style::color documentDarkColor(int32 colorIndex) { + static style::color colors[] = { st::msgFileBlueDark, st::msgFileGreenDark, st::msgFileRedDark, st::msgFileYellowDark }; + return colors[colorIndex & 3]; +} + +style::color documentOverColor(int32 colorIndex) { + static style::color colors[] = { st::msgFileBlueOver, st::msgFileGreenOver, st::msgFileRedOver, st::msgFileYellowOver }; + return colors[colorIndex & 3]; +} + +style::color documentSelectedColor(int32 colorIndex) { + static style::color colors[] = { st::msgFileBlueSelected, st::msgFileGreenSelected, st::msgFileRedSelected, st::msgFileYellowSelected }; + return colors[colorIndex & 3]; +} + style::sprite documentCorner(int32 colorIndex) { static style::sprite corners[] = { st::msgFileBlue, st::msgFileGreen, st::msgFileRed, st::msgFileYellow }; return corners[colorIndex & 3]; @@ -285,14 +300,14 @@ LayoutOverviewDate::LayoutOverviewDate(const QDate &date, bool month) void LayoutOverviewDate::initDimensions() { _maxw = st::normalFont->width(_text); - _minh = st::linksDateMargin + st::normalFont->height + st::linksDateMargin + st::linksBorder; + _minh = st::linksDateMargin.top() + st::normalFont->height + st::linksDateMargin.bottom() + st::linksBorder; } void LayoutOverviewDate::paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const { - if (clip.intersects(QRect(0, st::linksDateMargin, _width, st::normalFont->height))) { + if (clip.intersects(QRect(0, st::linksDateMargin.top(), _width, st::normalFont->height))) { p.setPen(st::linksDateColor); - p.setFont(st::normalFont); - p.drawTextLeft(0, st::linksDateMargin, _width, _text); + p.setFont(st::semiboldFont); + p.drawTextLeft(0, st::linksDateMargin.top(), _width, _text); } } @@ -629,12 +644,24 @@ void LayoutOverviewAudio::paint(Painter &p, const QRect &clip, uint32 selection, if (clip.intersects(rtlrect(nameleft, statustop, namewidth, st::normalFont->height, _width))) { p.setFont(st::normalFont); p.setPen(selected ? st::mediaInFgSelected : st::mediaInFg); + int32 unreadx = nameleft; if (_statusSize == FileStatusSizeLoaded || _statusSize == FileStatusSizeReady) { textstyleSet(&(selected ? st::mediaInStyleSelected : st::mediaInStyle)); _details.drawLeftElided(p, nameleft, statustop, namewidth, _width); textstyleRestore(); + unreadx += _details.maxWidth(); } else { - p.drawTextLeft(nameleft, statustop, _width, _statusText); + int32 statusw = st::normalFont->width(_statusText); + p.drawTextLeft(nameleft, statustop, _width, _statusText, statusw); + unreadx += statusw; + } + if (_parent->isMediaUnread() && unreadx + st::mediaUnreadSkip + st::mediaUnreadSize <= _width) { + p.setPen(Qt::NoPen); + p.setBrush(selected ? st::msgFileInBgSelected : st::msgFileInBg); + + p.setRenderHint(QPainter::HighQualityAntialiasing, true); + p.drawEllipse(rtlrect(unreadx + st::mediaUnreadSkip, statustop + st::mediaUnreadTop, st::mediaUnreadSize, st::mediaUnreadSize, _width)); + p.setRenderHint(QPainter::HighQualityAntialiasing, false); } } } @@ -715,6 +742,7 @@ LayoutOverviewDocument::LayoutOverviewDocument(DocumentData *document, HistoryIt , _data(document) , _msgl(new MessageLink(parent)) , _namel(new DocumentOpenLink(_data)) +, _thumbForLoaded(false) , _name(documentName(_data)) , _date(langDateTime(date(_data->date))) , _namew(st::semiboldFont->width(_name)) @@ -728,17 +756,17 @@ LayoutOverviewDocument::LayoutOverviewDocument(DocumentData *document, HistoryIt _data->thumb->load(); int32 tw = _data->thumb->width(), th = _data->thumb->height(); if (tw > th) { - _thumbw = (tw * st::msgFileThumbSize) / th; + _thumbw = (tw * st::overviewFileSize) / th; } else { - _thumbw = st::msgFileThumbSize; + _thumbw = st::overviewFileSize; } } else { _thumbw = 0; } _extw = st::semiboldFont->width(_ext); - if (_extw > st::msgFileThumbSize - st::msgFileExtPadding * 2) { - _ext = st::semiboldFont->elided(_ext, st::msgFileThumbSize - st::msgFileExtPadding * 2, Qt::ElideMiddle); + if (_extw > st::overviewFileSize - st::msgFileExtPadding * 2) { + _ext = st::semiboldFont->elided(_ext, st::overviewFileSize - st::msgFileExtPadding * 2, Qt::ElideMiddle); _extw = st::semiboldFont->width(_ext); } } @@ -748,7 +776,7 @@ void LayoutOverviewDocument::initDimensions() { if (_data->song()) { _minh = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom(); } else { - _minh = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom() + st::lineWidth; + _minh = st::overviewFilePadding.top() + st::overviewFileSize + st::overviewFilePadding.bottom() + st::lineWidth; } } @@ -817,39 +845,40 @@ void LayoutOverviewDocument::paint(Painter &p, const QRect &clip, uint32 selecti p.drawSpriteCenter(inner, icon); } } else { - nameleft = st::msgFileThumbSize + st::msgFileThumbPadding.right(); - nametop = st::linksBorder + st::msgFileThumbNameTop; - statustop = st::linksBorder + st::msgFileThumbStatusTop; - datetop = st::linksBorder + st::msgFileThumbLinkTop; + nameleft = st::overviewFileSize + st::overviewFilePadding.right(); + nametop = st::linksBorder + st::overviewFileNameTop; + statustop = st::linksBorder + st::overviewFileStatusTop; + datetop = st::linksBorder + st::overviewFileDateTop; - QRect shadow(rtlrect(nameleft, 0, _width - nameleft, st::linksBorder, _width)); - if (clip.intersects(shadow)) { - p.fillRect(clip.intersected(shadow), st::linksBorderFg); + const OverviewPaintContext *pcontext = context->toOverviewPaintContext(); + t_assert(pcontext != 0); + QRect border(rtlrect(nameleft, 0, _width - nameleft, st::linksBorder, _width)); + if (!pcontext->isAfterDate && clip.intersects(border)) { + p.fillRect(clip.intersected(border), st::linksBorderFg); } - QRect rthumb(rtlrect(0, st::linksBorder + st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, _width)); + QRect rthumb(rtlrect(0, st::linksBorder + st::overviewFilePadding.top(), st::overviewFileSize, st::overviewFileSize, _width)); if (clip.intersects(rthumb)) { if (wthumb) { if (_data->thumb->loaded()) { - QPixmap thumb = loaded ? _data->thumb->pixSingle(_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize) : _data->thumb->pixBlurredSingle(_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize); - p.drawPixmap(rthumb.topLeft(), thumb); + if (_thumb.isNull() || loaded != _thumbForLoaded) { + _thumbForLoaded = loaded; + _thumb = _data->thumb->pixNoCache(_thumbw, 0, true, !_thumbForLoaded, false, st::overviewFileSize, st::overviewFileSize); + } + p.drawPixmap(rthumb.topLeft(), _thumb); } else { - App::roundRect(p, rthumb, st::black, BlackCorners); + p.fillRect(rthumb, st::black); } } else { - App::roundRect(p, rthumb, documentColor(_colorIndex), documentCorners(_colorIndex)); - if (!radial && loaded) { - style::sprite icon = documentCorner(_colorIndex); - p.drawSprite(rthumb.topLeft() + QPoint(rtl() ? 0 : (rthumb.width() - icon.pxWidth()), 0), icon); - if (!_ext.isEmpty()) { - p.setFont(st::semiboldFont); - p.setPen(st::white); - p.drawText(rthumb.left() + (rthumb.width() - _extw) / 2, rthumb.top() + st::msgFileExtTop + st::semiboldFont->ascent, _ext); - } + p.fillRect(rthumb, documentColor(_colorIndex)); + if (!radial && loaded && !_ext.isEmpty()) { + p.setFont(st::semiboldFont); + p.setPen(st::white); + p.drawText(rthumb.left() + (rthumb.width() - _extw) / 2, rthumb.top() + st::msgFileExtTop + st::semiboldFont->ascent, _ext); } } if (selected) { - App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); + p.fillRect(rthumb, textstyleCurrent()->selectOverlay); } if (radial || (!loaded && !_data->loading())) { @@ -858,15 +887,19 @@ void LayoutOverviewDocument::paint(Painter &p, const QRect &clip, uint32 selecti float64 radialOpacity = (radial && loaded && !_data->uploading()) ? _radial->opacity() : 1; p.setPen(Qt::NoPen); if (selected) { - p.setBrush(st::msgDateImgBgSelected); + p.setBrush(wthumb ? st::msgDateImgBgSelected : documentSelectedColor(_colorIndex)); } else if (_a_iconOver.animating()) { _a_iconOver.step(context->ms); float64 over = a_iconOver.current(); - p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over)); - p.setBrush(st::black); + if (wthumb) { + p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over)); + p.setBrush(st::black); + } else { + p.setBrush(style::interpolate(documentDarkColor(_colorIndex), documentOverColor(_colorIndex), over)); + } } else { bool over = textlnkDrawOver(_data->loading() ? _cancell : _savel); - p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); + p.setBrush(over ? (wthumb ? st::msgDateImgBgOver : documentOverColor(_colorIndex)) : (wthumb ? st::msgDateImgBg : documentDarkColor(_colorIndex))); } p.setOpacity(radialOpacity * p.opacity()); @@ -890,10 +923,10 @@ void LayoutOverviewDocument::paint(Painter &p, const QRect &clip, uint32 selecti } } } - if (selected) { - p.drawSprite(rthumb.topLeft() + QPoint(rtl() ? 0 : (rthumb.width() - st::linksPhotoChecked.pxWidth()), rthumb.height() - st::linksPhotoChecked.pxHeight()), st::linksPhotoChecked); - } else if (context->selecting) { - p.drawSprite(rthumb.topLeft() + QPoint(rtl() ? 0 : (rthumb.width() - st::linksPhotoCheck.pxWidth()), rthumb.height() - st::linksPhotoCheck.pxHeight()), st::linksPhotoCheck); + if (selected || context->selecting) { + QRect check(rthumb.topLeft() + QPoint(rtl() ? 0 : (rthumb.width() - st::defaultCheckbox.diameter), rthumb.height() - st::defaultCheckbox.diameter), QSize(st::defaultCheckbox.diameter, st::defaultCheckbox.diameter)); + p.fillRect(check, selected ? st::overviewFileChecked : st::overviewFileCheck); + p.drawSpriteCenter(check, st::defaultCheckbox.checkIcon); } } } @@ -946,12 +979,12 @@ void LayoutOverviewDocument::getState(TextLinkPtr &link, HistoryCursorState &cur return; } } else { - nameleft = st::msgFileThumbSize + st::msgFileThumbPadding.right(); - nametop = st::linksBorder + st::msgFileThumbNameTop; - statustop = st::linksBorder + st::msgFileThumbStatusTop; - datetop = st::linksBorder + st::msgFileThumbLinkTop; + nameleft = st::overviewFileSize + st::overviewFilePadding.right(); + nametop = st::linksBorder + st::overviewFileNameTop; + statustop = st::linksBorder + st::overviewFileStatusTop; + datetop = st::linksBorder + st::overviewFileDateTop; - QRect rthumb(rtlrect(0, st::linksBorder + st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, _width)); + QRect rthumb(rtlrect(0, st::linksBorder + st::overviewFilePadding.top(), st::overviewFileSize, st::overviewFileSize, _width)); if (rthumb.contains(x, y)) { link = loaded ? _openl : ((_data->loading() || _data->status == FileUploading) ? _cancell : _savel); @@ -1150,7 +1183,7 @@ void LayoutOverviewLink::initDimensions() { _minh += qMin(3 * st::normalFont->height, _text.countHeight(_maxw - st::dlgPhotoSize - st::dlgPhotoPadding)); } _minh += _links.size() * st::normalFont->height; - _minh = qMax(_minh, int32(st::dlgPhotoSize)) + st::linksMargin * 2 + st::linksBorder; + _minh = qMax(_minh, int32(st::dlgPhotoSize)) + st::linksMargin.top() + st::linksMargin.bottom() + st::linksBorder; } int32 LayoutOverviewLink::resizeGetHeight(int32 width) { @@ -1168,12 +1201,12 @@ int32 LayoutOverviewLink::resizeGetHeight(int32 width) { _height += qMin(3 * st::normalFont->height, _text.countHeight(_width - st::dlgPhotoSize - st::dlgPhotoPadding)); } _height += _links.size() * st::normalFont->height; - _height = qMax(_height, int32(st::dlgPhotoSize)) + st::linksMargin * 2 + st::linksBorder; + _height = qMax(_height, int32(st::dlgPhotoSize)) + st::linksMargin.top() + st::linksMargin.bottom() + st::linksBorder; return _height; } void LayoutOverviewLink::paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const { - int32 left = st::dlgPhotoSize + st::dlgPhotoPadding, top = st::linksMargin + st::linksBorder, w = _width - left; + int32 left = st::dlgPhotoSize + st::dlgPhotoPadding, top = st::linksMargin.top() + st::linksBorder, w = _width - left; if (clip.intersects(rtlrect(0, top, st::dlgPhotoSize, st::dlgPhotoSize, _width))) { if (_page && _page->photo) { QPixmap pix; @@ -1213,6 +1246,8 @@ void LayoutOverviewLink::paint(Painter &p, const QRect &clip, uint32 selection, if (!_title.isEmpty() && _text.isEmpty() && _links.size() == 1) { top += (st::dlgPhotoSize - st::semiboldFont->height - st::normalFont->height) / 2; + } else { + top = st::linksTextTop; } p.setPen(st::black); @@ -1241,13 +1276,16 @@ void LayoutOverviewLink::paint(Painter &p, const QRect &clip, uint32 selection, top += st::normalFont->height; } - if (clip.intersects(rtlrect(left, 0, w, st::linksBorder, _width))) { - p.fillRect(clip.intersected(rtlrect(left, 0, w, st::linksBorder, _width)), st::linksBorderFg); + const OverviewPaintContext *pcontext = context->toOverviewPaintContext(); + t_assert(pcontext != 0); + QRect border(rtlrect(left, 0, w, st::linksBorder, _width)); + if (!pcontext->isAfterDate && clip.intersects(border)) { + p.fillRect(clip.intersected(border), st::linksBorderFg); } } void LayoutOverviewLink::getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { - int32 left = st::dlgPhotoSize + st::dlgPhotoPadding, top = st::linksMargin + st::linksBorder, w = _width - left; + int32 left = st::dlgPhotoSize + st::dlgPhotoPadding, top = st::linksMargin.top() + st::linksBorder, w = _width - left; if (rtlrect(0, top, st::dlgPhotoSize, st::dlgPhotoSize, _width).contains(x, y)) { link = _photol; return; @@ -1809,6 +1847,7 @@ void LayoutInlinePhoto::content_forget() { LayoutInlineWebVideo::LayoutInlineWebVideo(InlineResult *result) : LayoutInlineItem(result, 0, 0) , _send(new SendInlineItemLink()) +, _link(result->content_url.isEmpty() ? 0 : linkFromUrl(result->content_url)) , _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 (_result->duration) { @@ -1866,8 +1905,13 @@ void LayoutInlineWebVideo::paint(Painter &p, const QRect &clip, uint32 selection } void LayoutInlineWebVideo::getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { - if (x >= 0 && x < _width && y >= 0 && y < _height) { + 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; } } @@ -1896,6 +1940,7 @@ void LayoutInlineWebVideo::prepareThumb(int32 width, int32 height) const { LayoutInlineArticle::LayoutInlineArticle(InlineResult *result, bool withThumb) : LayoutInlineItem(result, 0, 0) , _send(new SendInlineItemLink()) , _url(result->url.isEmpty() ? 0 : linkFromUrl(result->url)) +, _link(result->content_url.isEmpty() ? 0 : linkFromUrl(result->content_url)) , _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) { @@ -1995,7 +2040,12 @@ void LayoutInlineArticle::paint(Painter &p, const QRect &clip, uint32 selection, } void LayoutInlineArticle::getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { - if (x >= 0 && x < _width && y >= 0 && y < _height) { + int32 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); @@ -2007,6 +2057,7 @@ void LayoutInlineArticle::getState(TextLinkPtr &link, HistoryCursorState &cursor } } link = _send; + return; } } diff --git a/Telegram/SourceFiles/layout.h b/Telegram/SourceFiles/layout.h index b069067382..0b346456c6 100644 --- a/Telegram/SourceFiles/layout.h +++ b/Telegram/SourceFiles/layout.h @@ -75,9 +75,13 @@ QString formatPlayedText(qint64 played, qint64 duration); QString documentName(DocumentData *document); int32 documentColorIndex(DocumentData *document, QString &ext); style::color documentColor(int32 colorIndex); +style::color documentDarkColor(int32 colorIndex); +style::color documentOverColor(int32 colorIndex); +style::color documentSelectedColor(int32 colorIndex); style::sprite documentCorner(int32 colorIndex); RoundCorners documentCorners(int32 colorIndex); +class OverviewPaintContext; class InlinePaintContext; class PaintContext { public: @@ -86,6 +90,10 @@ public: } uint64 ms; bool selecting; + + virtual const OverviewPaintContext *toOverviewPaintContext() const { + return 0; + } virtual const InlinePaintContext *toInlinePaintContext() const { return 0; } @@ -257,6 +265,17 @@ protected: }; +class OverviewPaintContext : public PaintContext { +public: + OverviewPaintContext(uint64 ms, bool selecting) : PaintContext(ms, selecting), isAfterDate(false) { + } + const OverviewPaintContext *toOverviewPaintContext() const { + return this; + } + bool isAfterDate; + +}; + class OverviewItemInfo { public: OverviewItemInfo() : _top(0) { @@ -267,7 +286,7 @@ public: void setTop(int32 top) { _top = top; } - + private: int32 _top; @@ -276,7 +295,7 @@ private: class LayoutOverviewDate : public LayoutItem { public: LayoutOverviewDate(const QDate &date, bool month); - + virtual void initDimensions(); virtual void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const; @@ -426,6 +445,9 @@ private: DocumentData *_data; TextLinkPtr _msgl, _namel; + mutable bool _thumbForLoaded; + mutable QPixmap _thumb; + QString _name, _date, _ext; int32 _namew, _datew, _extw; int32 _thumbw, _colorIndex; @@ -492,7 +514,7 @@ class LayoutInlineItem : public LayoutItem { public: LayoutInlineItem(InlineResult *result, DocumentData *doc, PhotoData *photo); - + virtual void setPosition(int32 position); int32 position() const; @@ -644,7 +666,7 @@ public: private: - TextLinkPtr _send; + TextLinkPtr _send, _link; mutable QPixmap _thumb; Text _title, _description; @@ -667,7 +689,7 @@ public: private: - TextLinkPtr _send, _url; + TextLinkPtr _send, _url, _link; bool _withThumb; mutable QPixmap _thumb; diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index d2889f7c6e..4f8f6dd03c 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -834,7 +834,7 @@ void OverviewInner::paintEvent(QPaintEvent *e) { p.setClipRect(r); } uint64 ms = getms(); - PaintContext context(ms, _selMode); + OverviewPaintContext context(ms, _selMode); if (_history->overview[_type].isEmpty() && (!_migrated || !_history->overviewLoaded(_type) || _migrated->overview[_type].isEmpty())) { QPoint dogPos((_width - st::msgDogImg.pxWidth()) / 2, ((height() - st::msgDogImg.pxHeight()) * 4) / 9); @@ -887,6 +887,7 @@ void OverviewInner::paintEvent(QPaintEvent *e) { if (_reversed) curY = _height - curY; if (_marginTop + curY >= r.y() + r.height()) break; + context.isAfterDate = (j > 0) ? !_items.at(j - 1)->toLayoutMediaItem() : false; p.translate(0, curY - y); _items.at(i)->paint(p, r.translated(-_rowsLeft, -_marginTop - curY), itemSelectedValue(i), &context); y = curY; diff --git a/Telegram/Telegram.plist b/Telegram/Telegram.plist index 5da30bd6da..544089e81f 100644 --- a/Telegram/Telegram.plist +++ b/Telegram/Telegram.plist @@ -11,7 +11,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.9.15 + 0.9.16 CFBundleSignature ???? CFBundleURLTypes diff --git a/Telegram/Telegram.rc b/Telegram/Telegram.rc index 944fa86c17..934f5c678e 100644 --- a/Telegram/Telegram.rc +++ b/Telegram/Telegram.rc @@ -34,8 +34,8 @@ IDI_ICON1 ICON "SourceFiles\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,9,15,7 - PRODUCTVERSION 0,9,15,7 + FILEVERSION 0,9,16,0 + PRODUCTVERSION 0,9,16,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -51,10 +51,10 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "Telegram Messenger LLP" - VALUE "FileVersion", "0.9.15.7" + VALUE "FileVersion", "0.9.16.0" VALUE "LegalCopyright", "Copyright (C) 2013" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "0.9.15.7" + VALUE "ProductVersion", "0.9.16.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Telegram.xcodeproj/project.pbxproj b/Telegram/Telegram.xcodeproj/project.pbxproj index 6d3bd7db4d..c8738b710d 100644 --- a/Telegram/Telegram.xcodeproj/project.pbxproj +++ b/Telegram/Telegram.xcodeproj/project.pbxproj @@ -1701,7 +1701,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.9.15; + CURRENT_PROJECT_VERSION = 0.9.16; DEBUG_INFORMATION_FORMAT = dwarf; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_OPTIMIZATION_LEVEL = 0; @@ -1720,7 +1720,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; COPY_PHASE_STRIP = YES; - CURRENT_PROJECT_VERSION = 0.9.15; + CURRENT_PROJECT_VERSION = 0.9.16; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_OPTIMIZATION_LEVEL = fast; GCC_PREFIX_HEADER = ./SourceFiles/stdafx.h; @@ -1747,10 +1747,10 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.9.15; + CURRENT_PROJECT_VERSION = 0.9.16; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DYLIB_COMPATIBILITY_VERSION = 0.9; - DYLIB_CURRENT_VERSION = 0.9.15; + DYLIB_CURRENT_VERSION = 0.9.16; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ""; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; @@ -1882,10 +1882,10 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.9.15; + CURRENT_PROJECT_VERSION = 0.9.16; DEBUG_INFORMATION_FORMAT = dwarf; DYLIB_COMPATIBILITY_VERSION = 0.9; - DYLIB_CURRENT_VERSION = 0.9.15; + DYLIB_CURRENT_VERSION = 0.9.16; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; FRAMEWORK_SEARCH_PATHS = ""; diff --git a/Telegram/Version b/Telegram/Version index b944a37222..8f5c67a00c 100644 --- a/Telegram/Version +++ b/Telegram/Version @@ -1,6 +1,6 @@ -AppVersion 9015 +AppVersion 9016 AppVersionStrMajor 0.9 -AppVersionStrSmall 0.9.15 -AppVersionStr 0.9.15 +AppVersionStrSmall 0.9.16 +AppVersionStr 0.9.16 DevChannel 0 -BetaVersion 9015007 +BetaVersion 0 9015008