diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index 726e6ac04..999bf5961 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -18,7 +18,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "history/history_item.h" #include "history/history_media_types.h" +#include "window/window_controller.h" #include "auth_session.h" +#include "mainwindow.h" #include "messenger.h" namespace { @@ -286,14 +288,14 @@ void DocumentOpenClickHandler::doOpen( } else if (data->size < App::kImageSizeLimit) { if (!data->data().isEmpty() && playAnimation) { if (action == ActionOnLoadPlayInline && context) { - Auth().data().requestItemPlayInline(context); + Auth().data().requestAnimationPlayInline(context); } else { Messenger::Instance().showDocument(data, context); } } else if (location.accessEnable()) { - if (data->isAnimation() || QImageReader(location.name()).canRead()) { - if (action == ActionOnLoadPlayInline && context) { - Auth().data().requestItemPlayInline(context); + if (playAnimation || QImageReader(location.name()).canRead()) { + if (playAnimation && action == ActionOnLoadPlayInline && context) { + Auth().data().requestAnimationPlayInline(context); } else { Messenger::Instance().showDocument(data, context); } @@ -592,7 +594,7 @@ void DocumentData::performActionOnLoad() { } else if (playAnimation) { if (loaded()) { if (_actionOnLoad == ActionOnLoadPlayInline && item) { - Auth().data().requestItemPlayInline(item); + Auth().data().requestAnimationPlayInline(item); } else { Messenger::Instance().showDocument(this, item); } @@ -610,11 +612,7 @@ void DocumentData::performActionOnLoad() { Auth().data().markMediaRead(this); } else if (loc.accessEnable()) { if (showImage && QImageReader(loc.name()).canRead()) { - if (_actionOnLoad == ActionOnLoadPlayInline && item) { - Auth().data().requestItemPlayInline(item); - } else { - Messenger::Instance().showDocument(this, item); - } + Messenger::Instance().showDocument(this, item); } else { File::Launch(already); } diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 247f90642..217fc9973 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -208,6 +208,7 @@ rpl::producer> Session::itemResizeRequest() const { void Session::requestViewResize(not_null view) { view->setPendingResize(); _viewResizeRequest.fire_copy(view); + notifyViewLayoutChange(view); } rpl::producer> Session::viewResizeRequest() const { @@ -225,12 +226,12 @@ rpl::producer> Session::itemViewRefreshRequest() const { return _itemViewRefreshRequest.events(); } -void Session::requestItemPlayInline(not_null item) { - _itemPlayInlineRequest.fire_copy(item); +void Session::requestAnimationPlayInline(not_null item) { + _animationPlayInlineRequest.fire_copy(item); } -rpl::producer> Session::itemPlayInlineRequest() const { - return _itemPlayInlineRequest.events(); +rpl::producer> Session::animationPlayInlineRequest() const { + return _animationPlayInlineRequest.events(); } void Session::notifyItemRemoved(not_null item) { @@ -1326,9 +1327,7 @@ void Session::unregisterAutoplayAnimation( void Session::stopAutoplayAnimations() { for (const auto [reader, view] : base::take(_autoplayAnimations)) { if (const auto media = view->media()) { - if (!media->isRoundVideoPlaying()) { - media->stopInline(); - } + media->stopAnimation(); } } } diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index 20a3ff435..fc270994e 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -72,8 +72,8 @@ public: rpl::producer> viewResizeRequest() const; void requestItemViewRefresh(not_null item); rpl::producer> itemViewRefreshRequest() const; - void requestItemPlayInline(not_null item); - rpl::producer> itemPlayInlineRequest() const; + void requestAnimationPlayInline(not_null item); + rpl::producer> animationPlayInlineRequest() const; void notifyHistoryUnloaded(not_null history); rpl::producer> historyUnloaded() const; @@ -445,7 +445,7 @@ private: rpl::event_stream> _itemResizeRequest; rpl::event_stream> _viewResizeRequest; rpl::event_stream> _itemViewRefreshRequest; - rpl::event_stream> _itemPlayInlineRequest; + rpl::event_stream> _animationPlayInlineRequest; rpl::event_stream> _itemRemoved; rpl::event_stream> _historyUnloaded; rpl::event_stream> _historyCleared; diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index c8021cc71..8d4c6ed39 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -239,16 +239,6 @@ bool isLayerShown() { return false; } -void autoplayMediaInlineAsync(const FullMsgId &msgId) { - if (auto main = App::main()) { - InvokeQueued(main, [msgId] { - if (const auto item = App::histItemById(msgId)) { - Auth().data().requestItemPlayInline(item); - } - }); - } -} - void showPeerProfile(const PeerId &peer) { if (auto window = App::wnd()) { if (auto controller = window->controller()) { @@ -340,33 +330,6 @@ void historyMuteUpdated(History *history) { if (MainWidget *m = App::main()) m->notify_historyMuteUpdated(history); } -//void handlePendingHistoryUpdate() { - //for (const auto item : base::take(Global::RefPendingRepaintItems())) { - // Auth().data().requestItemRepaint(item); - - // Start the video if it is waiting for that. - //if (item->pendingInitDimensions()) { // #TODO floating player video - // if (const auto media = item->getMedia()) { - // if (const auto reader = media->getClipReader()) { - // const auto startRequired = [&] { - // if (reader->started()) { - // return false; - // } - // using Mode = Media::Clip::Reader::Mode; - // return (reader->mode() == Mode::Video); - // }; - // if (startRequired()) { - // const auto width = std::max( - // item->width(), - // st::historyMinimalWidth); - // item->resizeGetHeight(width); - // } - // } - // } - //} - //} -//} - void unreadCounterUpdated() { Global::RefHandleUnreadCounterUpdate().call(); } diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index 8edddc3c0..8c2ad614b 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -182,8 +182,6 @@ void hideLayer(anim::type animated = anim::type::normal); void hideSettingsAndLayer(anim::type animated = anim::type::normal); bool isLayerShown(); -void autoplayMediaInlineAsync(const FullMsgId &msgId); - void showPeerProfile(const PeerId &peer); inline void showPeerProfile(const PeerData *peer) { showPeerProfile(peer->id); diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index 3640c7739..43167690b 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -231,11 +231,11 @@ InnerWidget::InnerWidget( refreshItem(view); } }, lifetime()); - Auth().data().itemPlayInlineRequest( + Auth().data().animationPlayInlineRequest( ) | rpl::start_with_next([this](auto item) { if (const auto view = viewForItem(item)) { if (const auto media = view->media()) { - media->playInline(true); + media->playAnimation(); } } }, lifetime()); @@ -474,6 +474,20 @@ std::unique_ptr InnerWidget::elementCreate( return std::make_unique(this, message); } +void InnerWidget::elementAnimationAutoplayAsync( + not_null view) { + crl::on_main(this, [this, msgId = view->data()->fullId()] { + if (const auto item = App::histItemById(msgId)) { + if (const auto view = viewForItem(item)) { + if (const auto media = view->media()) { + media->autoplayAnimation(); + } + } + } + }); +} + + void InnerWidget::saveState(not_null memento) { memento->setFilter(std::move(_filter)); memento->setAdmins(std::move(_admins)); diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h index 61af04f19..dc48a6faf 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h @@ -74,6 +74,8 @@ public: not_null message) override; std::unique_ptr elementCreate( not_null message) override; + void elementAnimationAutoplayAsync( + not_null element) override; ~InnerWidget(); diff --git a/Telegram/SourceFiles/history/feed/history_feed_section.cpp b/Telegram/SourceFiles/history/feed/history_feed_section.cpp index f2746ee56..af2a9f5b5 100644 --- a/Telegram/SourceFiles/history/feed/history_feed_section.cpp +++ b/Telegram/SourceFiles/history/feed/history_feed_section.cpp @@ -164,20 +164,6 @@ rpl::producer Widget::listSource( limitAfter); } -HistoryView::Context Widget::elementContext() { - return HistoryView::Context::Feed; -} - -std::unique_ptr Widget::elementCreate( - not_null message) { - return std::make_unique(this, message); -} - -std::unique_ptr Widget::elementCreate( - not_null message) { - return std::make_unique(this, message); -} - std::unique_ptr Widget::createMemento() { auto result = std::make_unique(_feed); saveState(result.get()); diff --git a/Telegram/SourceFiles/history/feed/history_feed_section.h b/Telegram/SourceFiles/history/feed/history_feed_section.h index eb06d6d84..c4964b304 100644 --- a/Telegram/SourceFiles/history/feed/history_feed_section.h +++ b/Telegram/SourceFiles/history/feed/history_feed_section.h @@ -67,13 +67,6 @@ public: int limitBefore, int limitAfter) override; - // HistoryView::ElementDelegate interface. - HistoryView::Context elementContext() override; - std::unique_ptr elementCreate( - not_null message) override; - std::unique_ptr elementCreate( - not_null message) override; - protected: void resizeEvent(QResizeEvent *e) override; void paintEvent(QPaintEvent *e) override; diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 26270f0ef..fb0745103 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -2922,6 +2922,18 @@ not_null HistoryInner::ElementDelegate() { not_null message) override { return std::make_unique(this, message); } + void elementAnimationAutoplayAsync( + not_null view) override { + crl::on_main(&Auth(), [msgId = view->data()->fullId()] { + if (const auto item = App::histItemById(msgId)) { + if (const auto view = item->mainView()) { + if (const auto media = view->media()) { + media->autoplayAnimation(); + } + } + } + }); + } }; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 65d977451..df455912a 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -639,33 +639,6 @@ bool HistoryItem::isEmpty() const { && !Has(); } -void HistoryItem::audioTrackUpdated() { - auto media = this->media(); - if (!media) { - return; - } - - // #TODO GIFs - //auto reader = media->getClipReader(); - //if (!reader || reader->mode() != Media::Clip::Reader::Mode::Video) { - // return; - //} - - //auto audio = reader->audioMsgId(); - //auto current = Media::Player::mixer()->currentState(audio.type()); - //if (current.id != audio || Media::Player::IsStoppedOrStopping(current.state)) { - // media->stopInline(); - //} else if (Media::Player::IsPaused(current.state) || current.state == Media::Player::State::Pausing) { - // if (!reader->videoPaused()) { - // reader->pauseResumeVideo(); - // } - //} else { - // if (reader->videoPaused()) { - // reader->pauseResumeVideo(); - // } - //} -} - HistoryItem *HistoryItem::previousItem() const { if (const auto view = mainView()) { if (const auto previous = view->previousInBlocks()) { diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 629c93294..d3538e5c3 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -266,8 +266,6 @@ public: MessageGroupId groupId() const; - void audioTrackUpdated(); - HistoryItem *previousItem() const; HistoryItem *nextItem() const; diff --git a/Telegram/SourceFiles/history/history_media.h b/Telegram/SourceFiles/history/history_media.h index 64d8f07f6..c27508765 100644 --- a/Telegram/SourceFiles/history/history_media.h +++ b/Telegram/SourceFiles/history/history_media.h @@ -130,20 +130,14 @@ public: virtual DocumentData *getDocument() const { return nullptr; } - virtual Media::Clip::Reader *getClipReader() { - return nullptr; - } - bool playInline(/*bool autoplay = false*/) { - return playInline(false); + void playAnimation() { + playAnimation(false); } - virtual bool playInline(bool autoplay) { - return false; + void autoplayAnimation() { + playAnimation(true); } - virtual void stopInline() { - } - virtual bool isRoundVideoPlaying() const { - return false; + virtual void stopAnimation() { } virtual QSize sizeForGrouping() const { @@ -243,6 +237,9 @@ protected: QSize countCurrentSize(int newWidth) override; Text createCaption(not_null item) const; + virtual void playAnimation(bool autoplay) { + } + not_null _parent; MediaInBubbleState _inBubbleState = MediaInBubbleState::None; diff --git a/Telegram/SourceFiles/history/history_media_types.cpp b/Telegram/SourceFiles/history/history_media_types.cpp index 0fda3d693..8442a8e93 100644 --- a/Telegram/SourceFiles/history/history_media_types.cpp +++ b/Telegram/SourceFiles/history/history_media_types.cpp @@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/media_audio.h" #include "media/media_clip_reader.h" #include "media/player/media_player_instance.h" +#include "media/player/media_player_round_controller.h" #include "media/view/media_clip_playback.h" #include "boxes/confirm_box.h" #include "boxes/add_contact_box.h" @@ -1918,17 +1919,6 @@ void HistoryDocument::refreshParentId(not_null realParent) { } } -bool HistoryDocument::playInline(bool autoplay) { - if (_data->isVoiceMessage()) { - DocumentOpenClickHandler::doOpen(_data, _parent->data(), ActionOnLoadPlayInline); - return true; - } else if (_data->isAudioFile()) { - Media::Player::instance()->play(AudioMsgId(_data, _parent->data()->fullId())); - return true; - } - return false; -} - TextWithEntities HistoryDocument::getCaption() const { if (const auto captioned = Get()) { return captioned->_caption.originalTextWithEntities(); @@ -1979,9 +1969,10 @@ QSize HistoryGif::countOptimalSize() { setClipReader(Media::Clip::ReaderPointer::Bad()); } - if (_gif && _gif->ready()) { - tw = convertScale(_gif->width()); - th = convertScale(_gif->height()); + const auto reader = currentReader(); + if (reader) { + tw = convertScale(reader->width()); + th = convertScale(reader->height()); } else { tw = convertScale(_data->dimensions.width()), th = convertScale(_data->dimensions.height()); if (!tw || !th) { @@ -2005,7 +1996,7 @@ QSize HistoryGif::countOptimalSize() { auto maxWidth = qMax(tw, st::minPhotoSize); auto minHeight = qMax(th, st::minPhotoSize); accumulate_max(maxWidth, _parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); - if (!_gif || !_gif->ready()) { + if (!reader) { accumulate_max(maxWidth, gifMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); } if (_parent->hasBubble()) { @@ -2033,9 +2024,10 @@ QSize HistoryGif::countCurrentSize(int newWidth) { auto availableWidth = newWidth; int tw = 0, th = 0; - if (_gif && _gif->ready()) { - tw = convertScale(_gif->width()); - th = convertScale(_gif->height()); + const auto reader = currentReader(); + if (reader) { + tw = convertScale(reader->width()); + th = convertScale(reader->height()); } else { tw = convertScale(_data->dimensions.width()), th = convertScale(_data->dimensions.height()); if (!tw || !th) { @@ -2065,14 +2057,31 @@ QSize HistoryGif::countCurrentSize(int newWidth) { newWidth = qMax(tw, st::minPhotoSize); auto newHeight = qMax(th, st::minPhotoSize); accumulate_max(newWidth, _parent->infoWidth() + 2 * st::msgDateImgDelta + st::msgDateImgPadding.x()); - if (_gif && _gif->ready()) { - if (!_gif->started()) { + if (reader) { + const auto own = (reader->mode() == Media::Clip::Reader::Mode::Gif); + if (own && !reader->started()) { auto isRound = _data->isVideoMessage(); auto inWebPage = (_parent->media() != this); - auto roundRadius = isRound ? ImageRoundRadius::Ellipse : inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large; - auto roundCorners = (isRound || inWebPage) ? RectPart::AllCorners : ((isBubbleTop() ? (RectPart::TopLeft | RectPart::TopRight) : RectPart::None) - | ((isBubbleBottom() && _caption.isEmpty()) ? (RectPart::BottomLeft | RectPart::BottomRight) : RectPart::None)); - _gif->start(_thumbw, _thumbh, newWidth, newHeight, roundRadius, roundCorners); + auto roundRadius = isRound + ? ImageRoundRadius::Ellipse + : inWebPage + ? ImageRoundRadius::Small + : ImageRoundRadius::Large; + auto roundCorners = (isRound || inWebPage) + ? RectPart::AllCorners + : ((isBubbleTop() + ? (RectPart::TopLeft | RectPart::TopRight) + : RectPart::None) + | ((isBubbleBottom() && _caption.isEmpty()) + ? (RectPart::BottomLeft | RectPart::BottomRight) + : RectPart::None)); + reader->start( + _thumbw, + _thumbh, + newWidth, + newHeight, + roundRadius, + roundCorners); } } else { accumulate_max(newWidth, gifMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); @@ -2117,11 +2126,12 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM auto displayLoading = (item->id < 0) || _data->displayLoading(); auto selected = (selection == FullSelection); - auto videoFinished = _gif - && (_gif->mode() == Media::Clip::Reader::Mode::Video) - && (_gif->state() == Media::Clip::State::Finished); - if (loaded && cAutoPlayGif() && ((!_gif && !_gif.isBad()) || videoFinished)) { - Ui::autoplayMediaInlineAsync(item->fullId()); + if (loaded + && cAutoPlayGif() + && !_gif + && !_gif.isBad() + && !activeRoundVideo()) { + _parent->delegate()->elementAnimationAutoplayAsync(_parent); } auto paintx = 0, painty = 0, paintw = width(), painth = height(); @@ -2133,7 +2143,11 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM auto isRound = _data->isVideoMessage(); auto displayMute = false; - auto animating = (_gif && _gif->started()); + const auto reader = currentReader(); + const auto playingVideo = reader + ? (reader->mode() == Media::Clip::Reader::Mode::Video) + : false; + const auto animating = reader && reader->started(); if (!animating || item->id < 0) { if (displayLoading) { @@ -2143,7 +2157,7 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM } } updateStatusText(); - } else if (_gif && _gif->mode() == Media::Clip::Reader::Mode::Video) { + } else if (playingVideo) { updateStatusText(); } auto radial = isRadialAnimation(ms); @@ -2180,18 +2194,16 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM if (animating) { auto paused = App::wnd()->controller()->isGifPausedAtLeastFor(Window::GifPauseReason::Any); if (isRound) { - if (_gif->mode() == Media::Clip::Reader::Mode::Video) { + if (playingVideo) { paused = false; } else { displayMute = true; } } - p.drawPixmap(rthumb.topLeft(), _gif->current(_thumbw, _thumbh, usew, painth, roundRadius, roundCorners, paused ? 0 : ms)); + p.drawPixmap(rthumb.topLeft(), reader->current(_thumbw, _thumbh, usew, painth, roundRadius, roundCorners, paused ? 0 : ms)); - if (displayMute) { - _roundPlayback.reset(); - } else if (_roundPlayback) { - auto value = _roundPlayback->value(ms); + if (const auto playback = videoPlayback()) { + const auto value = playback->value(ms); if (value > 0.) { auto pen = st::historyVideoMessageProgressFg->p; auto was = p.pen(); @@ -2220,7 +2232,7 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM App::complexOverlayRect(p, rthumb, roundRadius, roundCorners); } - if (radial || _gif.isBad() || (!_gif && ((!loaded && !_data->loading()) || !cAutoPlayGif()))) { + if (radial || (!reader && (_gif.isBad() || (!loaded && !_data->loading()) || !cAutoPlayGif()))) { auto radialOpacity = (radial && loaded && item->id > 0) ? _animation->radial.opacity() : 1.; auto inner = QRect(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize); p.setPen(Qt::NoPen); @@ -2594,23 +2606,19 @@ void HistoryGif::updateStatusText() const { statusSize = _data->loadOffset(); } else if (_data->loaded()) { statusSize = FileStatusSizeLoaded; - if (_gif && _gif->mode() == Media::Clip::Reader::Mode::Video) { + if (const auto video = activeRoundPlayer()) { statusSize = -1 - _data->duration(); - auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Voice); - if (state.id == _gif->audioMsgId()) { - if (state.length) { - auto position = int64(0); - if (Media::Player::IsStoppedAtEnd(state.state)) { - position = state.length; - } else if (!Media::Player::IsStoppedOrStopping(state.state)) { - position = state.position; - } - accumulate_max(statusSize, -1 - int((state.length - position) / state.frequency + 1)); - } - if (_roundPlayback) { - _roundPlayback->updateState(state); + const auto state = Media::Player::mixer()->currentState( + AudioMsgId::Type::Voice); + if (state.id == video->audioMsgId() && state.length) { + auto position = int64(0); + if (Media::Player::IsStoppedAtEnd(state.state)) { + position = state.length; + } else if (!Media::Player::IsStoppedOrStopping(state.state)) { + position = state.position; } + accumulate_max(statusSize, -1 - int((state.length - position) / state.frequency + 1)); } } } else { @@ -2646,63 +2654,96 @@ int HistoryGif::additionalWidth(const HistoryMessageVia *via, const HistoryMessa return result; } -bool HistoryGif::playInline(bool autoplay) { - using Mode = Media::Clip::Reader::Mode; - if (_data->isVideoMessage() && _gif) { - // Stop autoplayed silent video when we start playback by click. - // Stop finished video message when autoplay starts. - if (!autoplay) { - if (_gif->mode() == Mode::Gif) { - stopInline(); - } else { - _gif->pauseResumeVideo(); - return true; +Media::Player::RoundController *HistoryGif::activeRoundVideo() const { + return App::wnd()->controller()->roundVideo(_parent->data()); +} + +Media::Clip::Reader *HistoryGif::activeRoundPlayer() const { + if (const auto video = activeRoundVideo()) { + if (const auto result = video->reader()) { + if (result->ready()) { + return result; } - } else if (autoplay && _gif->mode() == Mode::Video && _gif->state() == Media::Clip::State::Finished) { - stopInline(); } } + return nullptr; +} + +Media::Clip::Reader *HistoryGif::currentReader() const { + if (const auto result = activeRoundPlayer()) { + return result; + } + return (_gif && _gif->ready()) ? _gif.get() : nullptr; +} + +Media::Clip::Playback *HistoryGif::videoPlayback() const { + if (const auto video = activeRoundVideo()) { + return video->playback(); + } + return nullptr; +} + +void HistoryGif::clipCallback(Media::Clip::Notification notification) { + using namespace Media::Clip; + + const auto reader = _gif.get(); + if (!reader) { + return; + } + switch (notification) { + case NotificationReinit: { + auto stopped = false; + if (reader->autoPausedGif()) { + auto amVisible = false; + Auth().data().queryItemVisibility().notify( + { _parent->data(), &amVisible }, + true); + if (!amVisible) { // Stop animation if it is not visible. + stopAnimation(); + stopped = true; + } + } + if (!stopped) { + Auth().data().requestViewResize(_parent); + } + } break; + + case NotificationRepaint: { + if (!reader->currentDisplayed()) { + Auth().data().requestViewRepaint(_parent); + } + } break; + } +} + +void HistoryGif::playAnimation(bool autoplay) { + if (_data->isVideoMessage() && !autoplay) { + return; + } + using Mode = Media::Clip::Reader::Mode; if (_gif) { - stopInline(); + stopAnimation(); } else if (_data->loaded(DocumentData::FilePathResolveChecked)) { if (!cAutoPlayGif()) { Auth().data().stopAutoplayAnimations(); } - const auto mode = (!autoplay && _data->isVideoMessage()) - ? Mode::Video - : Mode::Gif; - setClipReader(Media::Clip::MakeReader(_data, _parent->data()->fullId(), [this](Media::Clip::Notification notification) { - _parent->clipCallback(notification); - }, mode)); - if (mode == Mode::Video) { - _roundPlayback = std::make_unique(); - _roundPlayback->setValueChangedCallback([this](float64 value) { - Auth().data().requestViewRepaint(_parent); - }); - if (App::main()) { - Auth().data().markMediaRead(_data); - } - App::wnd()->controller()->enableGifPauseReason(Window::GifPauseReason::RoundPlaying); - } + setClipReader(Media::Clip::MakeReader( + _data, + _parent->data()->fullId(), + [=](auto notification) { clipCallback(notification); }, + Mode::Gif)); if (_gif && autoplay) { _gif->setAutoplay(); } } - return true; } -bool HistoryGif::isRoundVideoPlaying() const { - return (_gif && _gif->mode() == Media::Clip::Reader::Mode::Video); -} - -void HistoryGif::stopInline() { - if (isRoundVideoPlaying()) { - App::wnd()->controller()->disableGifPauseReason(Window::GifPauseReason::RoundPlaying); +void HistoryGif::stopAnimation() { + if (_gif) { + clearClipReader(); + Auth().data().requestViewResize(_parent); + _data->forget(); } - clearClipReader(); - - Auth().data().requestViewResize(_parent); - Auth().data().notifyViewLayoutChange(_parent); } void HistoryGif::setClipReader(Media::Clip::ReaderPointer gif) { @@ -3805,6 +3846,17 @@ void HistoryWebPage::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool p _attach->clickHandlerPressedChanged(p, pressed); } } + +void HistoryWebPage::playAnimation(bool autoplay) { + if (_attach) { + if (autoplay) { + _attach->autoplayAnimation(); + } else { + _attach->playAnimation(); + } + } +} + bool HistoryWebPage::isDisplayed() const { const auto item = _parent->data(); return !_data->pendingTill @@ -4228,6 +4280,16 @@ ImagePtr HistoryGame::replyPreview() { return _attach ? _attach->replyPreview() : (_data->photo ? _data->photo->makeReplyPreview() : ImagePtr()); } +void HistoryGame::playAnimation(bool autoplay) { + if (_attach) { + if (autoplay) { + _attach->autoplayAnimation(); + } else { + _attach->playAnimation(); + } + } +} + QMargins HistoryGame::inBubblePadding() const { auto lshift = st::msgPadding.left() + st::webPageLeft; auto rshift = st::msgPadding.right(); diff --git a/Telegram/SourceFiles/history/history_media_types.h b/Telegram/SourceFiles/history/history_media_types.h index 6e56fbab4..9e1d07c29 100644 --- a/Telegram/SourceFiles/history/history_media_types.h +++ b/Telegram/SourceFiles/history/history_media_types.h @@ -31,6 +31,10 @@ namespace Media { namespace Clip { class Playback; } // namespace Clip + +namespace Player { +class RoundController; +} // namespace Player } // namespace Media namespace Ui { @@ -364,8 +368,6 @@ public: return _data; } - bool playInline(bool autoplay) override; - bool hasReplyPreview() const override { return !_data->thumb->isNull(); } @@ -447,13 +449,8 @@ public: DocumentData *getDocument() const override { return _data; } - Media::Clip::Reader *getClipReader() override { - return _gif.get(); - } - bool playInline(bool autoplay) override; - void stopInline() override; - bool isRoundVideoPlaying() const override; + void stopAnimation() override; bool hasReplyPreview() const override { return !_data->thumb->isNull(); @@ -489,8 +486,14 @@ protected: } private: + void playAnimation(bool autoplay) override; QSize countOptimalSize() override; QSize countCurrentSize(int newWidth) override; + Media::Player::RoundController *activeRoundVideo() const; + Media::Clip::Reader *activeRoundPlayer() const; + Media::Clip::Reader *currentReader() const; + Media::Clip::Playback *videoPlayback() const; + void clipCallback(Media::Clip::Notification notification); bool needInfoDisplay() const; int additionalWidth( @@ -506,8 +509,6 @@ private: int _thumbw = 1; int _thumbh = 1; Text _caption; - - mutable std::unique_ptr _roundPlayback; Media::Clip::ReaderPointer _gif; void setStatusSize(int newSize) const; @@ -732,14 +733,8 @@ public: DocumentData *getDocument() const override { return _attach ? _attach->getDocument() : nullptr; } - Media::Clip::Reader *getClipReader() override { - return _attach ? _attach->getClipReader() : nullptr; - } - bool playInline(bool autoplay) override { - return _attach ? _attach->playInline(autoplay) : false; - } - void stopInline() override { - if (_attach) _attach->stopInline(); + void stopAnimation() override { + if (_attach) _attach->stopAnimation(); } bool hasReplyPreview() const override; @@ -766,6 +761,7 @@ public: ~HistoryWebPage(); private: + void playAnimation(bool autoplay) override; QSize countOptimalSize() override; QSize countCurrentSize(int newWidth) override; @@ -842,14 +838,8 @@ public: DocumentData *getDocument() const override { return _attach ? _attach->getDocument() : nullptr; } - Media::Clip::Reader *getClipReader() override { - return _attach ? _attach->getClipReader() : nullptr; - } - bool playInline(bool autoplay) override { - return _attach ? _attach->playInline(autoplay) : false; - } - void stopInline() override { - if (_attach) _attach->stopInline(); + void stopAnimation() override { + if (_attach) _attach->stopAnimation(); } bool hasReplyPreview() const override { @@ -878,6 +868,7 @@ public: ~HistoryGame(); private: + void playAnimation(bool autoplay) override; QSize countOptimalSize() override; QSize countCurrentSize(int newWidth) override; diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index d71ad0daf..a24980249 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -573,11 +573,11 @@ HistoryWidget::HistoryWidget(QWidget *parent, not_null cont ) | rpl::start_with_next([this](auto item) { item->refreshMainView(); }, lifetime()); - Auth().data().itemPlayInlineRequest( + Auth().data().animationPlayInlineRequest( ) | rpl::start_with_next([this](auto item) { if (const auto view = item->mainView()) { if (const auto media = view->media()) { - media->playInline(true); + media->playAnimation(); } } }, lifetime()); diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index fa18af56c..b31938808 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "history/view/history_view_element.h" -#include "media/media_clip_reader.h" #include "history/history_item_components.h" #include "history/history_item.h" #include "history/history_media.h" @@ -191,51 +190,6 @@ void Element::nextInBlocksRemoved() { setAttachToNext(false); } -void Element::clipCallback(Media::Clip::Notification notification) { - using namespace Media::Clip; - - const auto media = this->media(); - if (!media) { - return; - } - - const auto reader = media->getClipReader(); - if (!reader) { - return; - } - - switch (notification) { - case NotificationReinit: { - auto stopped = false; - if (reader->autoPausedGif()) { - auto amVisible = false; - Auth().data().queryItemVisibility().notify({ data(), &amVisible }, true); - if (!amVisible) { // stop animation if it is not visible - media->stopInline(); - if (const auto document = media->getDocument()) { - document->forget(); - } - stopped = true; - } - } else if (reader->mode() == Reader::Mode::Video - && reader->state() == State::Finished) { - // Stop finished video message. - media->stopInline(); - } - if (!stopped) { - Auth().data().requestViewResize(this); - Auth().data().notifyViewLayoutChange(this); - } - } break; - - case NotificationRepaint: { - if (!reader->currentDisplayed()) { - Auth().data().requestViewRepaint(this); - } - } break; - } -} - void Element::refreshDataId() { if (const auto media = this->media()) { media->refreshParentId(data()); diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h index d35b28711..6d7a5096f 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.h +++ b/Telegram/SourceFiles/history/view/history_view_element.h @@ -37,6 +37,8 @@ public: not_null message) = 0; virtual std::unique_ptr elementCreate( not_null message) = 0; + virtual void elementAnimationAutoplayAsync( + not_null element) = 0; }; @@ -180,8 +182,6 @@ public: void previousInBlocksChanged(); void nextInBlocksRemoved(); - void clipCallback(Media::Clip::Notification notification); - virtual ~Element(); protected: diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index b4d05fc61..e176ece3a 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_message.h" #include "history/history_item_components.h" #include "history/view/history_view_element.h" +#include "history/view/history_view_message.h" #include "history/view/history_view_service_message.h" #include "chat_helpers/message_field.h" #include "mainwindow.h" @@ -223,13 +224,13 @@ ListWidget::ListWidget( _scrollDateHideTimer.setCallback([this] { scrollDateHideByTimer(); }); Auth().data().viewRepaintRequest( ) | rpl::start_with_next([this](auto view) { - if (view->delegate() == _delegate) { + if (view->delegate() == this) { repaintItem(view); } }, lifetime()); Auth().data().viewResizeRequest( ) | rpl::start_with_next([this](auto view) { - if (view->delegate() == _delegate) { + if (view->delegate() == this) { updateSize(); } }, lifetime()); @@ -239,11 +240,11 @@ ListWidget::ListWidget( refreshItem(view); } }, lifetime()); - Auth().data().itemPlayInlineRequest( + Auth().data().animationPlayInlineRequest( ) | rpl::start_with_next([this](auto item) { if (const auto view = viewForItem(item)) { if (const auto media = view->media()) { - media->playInline(true); + media->playAnimation(); } } }, lifetime()); @@ -329,7 +330,7 @@ not_null ListWidget::enforceViewForItem( } const auto [i, ok] = _views.emplace( item, - item->createView(_delegate)); + item->createView(this)); return i->second.get(); } @@ -511,6 +512,33 @@ QPoint ListWidget::tooltipPos() const { return _mousePosition; } +Context ListWidget::elementContext() { + return _delegate->listContext(); +} + +std::unique_ptr ListWidget::elementCreate( + not_null message) { + return std::make_unique(this, message); +} + +std::unique_ptr ListWidget::elementCreate( + not_null message) { + return std::make_unique(this, message); +} + +void ListWidget::elementAnimationAutoplayAsync( + not_null view) { + crl::on_main(this, [this, msgId = view->data()->fullId()]{ + if (const auto item = App::histItemById(msgId)) { + if (const auto view = viewForItem(item)) { + if (const auto media = view->media()) { + media->autoplayAnimation(); + } + } + } + }); +} + void ListWidget::saveState(not_null memento) { memento->setAroundPosition(_aroundPosition); auto state = countScrollState(); @@ -1432,7 +1460,7 @@ void ListWidget::refreshItem(not_null view) { _views.erase(item); const auto [i, ok] = _views.emplace( item, - item->createView(_delegate)); + item->createView(this)); const auto was = view; const auto now = i->second.get(); _items[index] = now; diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index c2d007c9d..c88d527b6 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -27,7 +27,7 @@ namespace HistoryView { enum class Context : char; -class ListDelegate : public ElementDelegate { +class ListDelegate { public: virtual Context listContext() = 0; virtual void listScrollTo(int top) = 0; @@ -77,6 +77,7 @@ private: class ListWidget final : public Ui::RpWidget + , public ElementDelegate , public Ui::AbstractTooltipShower , private base::Subscriber { public: @@ -100,6 +101,15 @@ public: QString tooltipText() const override; QPoint tooltipPos() const override; + // ElementDelegate interface. + Context elementContext() override; + std::unique_ptr elementCreate( + not_null message) override; + std::unique_ptr elementCreate( + not_null message) override; + void elementAnimationAutoplayAsync( + not_null view) override; + ~ListWidget(); protected: diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 788c44512..e2981f1db 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -171,15 +171,25 @@ StackItemSection::StackItemSection( } template -MainWidget::Float::Float(QWidget *parent, HistoryItem *item, ToggleCallback toggle, DraggedCallback dragged) +MainWidget::Float::Float( + QWidget *parent, + not_null controller, + not_null item, + ToggleCallback toggle, + DraggedCallback dragged) : animationSide(RectPart::Right) , column(Window::Column::Second) , corner(RectPart::TopRight) -, widget(parent, item, [this, toggle = std::move(toggle)](bool visible) { - toggle(this, visible); -}, [this, dragged = std::move(dragged)](bool closed) { - dragged(this, closed); -}) { +, widget( + parent, + controller, + item, + [this, toggle = std::move(toggle)](bool visible) { + toggle(this, visible); + }, + [this, dragged = std::move(dragged)](bool closed) { + dragged(this, closed); + }) { } MainWidget::MainWidget( @@ -317,33 +327,45 @@ MainWidget::MainWidget( } void MainWidget::checkCurrentFloatPlayer() { - auto state = Media::Player::instance()->current(AudioMsgId::Type::Voice); - auto fullId = state.contextId(); - auto last = currentFloatPlayer(); - if (!last || last->widget->detached() || last->widget->item()->fullId() != fullId) { - if (last) { - last->widget->detach(); - } - if (auto item = App::histItemById(fullId)) { - if (auto media = item->media()) { - if (auto document = media->document()) { - if (document->isVideoMessage()) { - _playerFloats.push_back(std::make_unique(this, item, [this](not_null instance, bool visible) { - instance->hiddenByWidget = !visible; - toggleFloatPlayer(instance); - }, [this](not_null instance, bool closed) { - finishFloatPlayerDrag(instance, closed); - })); - currentFloatPlayer()->column = Auth().settings().floatPlayerColumn(); - currentFloatPlayer()->corner = Auth().settings().floatPlayerCorner(); - checkFloatPlayerVisibility(); - } + const auto state = Media::Player::instance()->current(AudioMsgId::Type::Voice); + const auto fullId = state.contextId(); + const auto last = currentFloatPlayer(); + if (last + && !last->widget->detached() + && last->widget->item()->fullId() == fullId) { + return; + } + if (last) { + last->widget->detach(); + } + if (const auto item = App::histItemById(fullId)) { + if (const auto media = item->media()) { + if (const auto document = media->document()) { + if (document->isVideoMessage()) { + createFloatPlayer(item); } } } } } +void MainWidget::createFloatPlayer(not_null item) { + _playerFloats.push_back(std::make_unique( + this, + _controller, + item, + [this](not_null instance, bool visible) { + instance->hiddenByWidget = !visible; + toggleFloatPlayer(instance); + }, + [this](not_null instance, bool closed) { + finishFloatPlayerDrag(instance, closed); + })); + currentFloatPlayer()->column = Auth().settings().floatPlayerColumn(); + currentFloatPlayer()->corner = Auth().settings().floatPlayerCorner(); + checkFloatPlayerVisibility(); +} + void MainWidget::toggleFloatPlayer(not_null instance) { auto visible = !instance->hiddenByHistory && !instance->hiddenByWidget && instance->widget->isReady(); if (instance->visible != visible) { @@ -1579,7 +1601,7 @@ void MainWidget::handleAudioUpdate(const AudioMsgId &audioId) { } } - if (state.id == audioId && (audioId.type() == AudioMsgId::Type::Song || audioId.type() == AudioMsgId::Type::Voice)) { + if (state.id == audioId) { if (!Media::Player::IsStoppedOrStopping(state.state)) { createPlayer(); } @@ -1587,7 +1609,6 @@ void MainWidget::handleAudioUpdate(const AudioMsgId &audioId) { if (const auto item = App::histItemById(audioId.contextId())) { Auth().data().requestItemRepaint(item); - item->audioTrackUpdated(); } if (const auto items = InlineBots::Layout::documentItems()) { if (const auto i = items->find(document); i != items->end()) { diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index a6d0af5bc..c0d73ea27 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -380,7 +380,12 @@ protected: private: struct Float { template - Float(QWidget *parent, HistoryItem *item, ToggleCallback callback, DraggedCallback dragged); + Float( + QWidget *parent, + not_null controller, + not_null item, + ToggleCallback toggle, + DraggedCallback dragged); bool hiddenByWidget = false; bool hiddenByHistory = false; @@ -506,6 +511,7 @@ private: void clearCachedBackground(); void checkCurrentFloatPlayer(); + void createFloatPlayer(not_null item); void toggleFloatPlayer(not_null instance); void checkFloatPlayerVisibility(); void updateFloatPlayerPosition(not_null instance); diff --git a/Telegram/SourceFiles/media/player/media_player_float.cpp b/Telegram/SourceFiles/media/player/media_player_float.cpp index d0c54a7f7..b773ff542 100644 --- a/Telegram/SourceFiles/media/player/media_player_float.cpp +++ b/Telegram/SourceFiles/media/player/media_player_float.cpp @@ -15,8 +15,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item.h" #include "history/view/history_view_element.h" #include "media/media_clip_reader.h" -#include "media/view/media_clip_playback.h" #include "media/media_audio.h" +#include "media/view/media_clip_playback.h" +#include "media/player/media_player_round_controller.h" +#include "window/window_controller.h" #include "auth_session.h" #include "styles/style_media_player.h" #include "styles/style_history.h" @@ -26,10 +28,12 @@ namespace Player { Float::Float( QWidget *parent, - HistoryItem *item, + not_null controller, + not_null item, base::lambda toggleCallback, base::lambda draggedCallback) : RpWidget(parent) +, _controller(controller) , _item(item) , _toggleCallback(std::move(toggleCallback)) , _draggedCallback(std::move(draggedCallback)) { @@ -47,7 +51,7 @@ Float::Float( prepareShadow(); rpl::merge( - //Auth().data().viewLayoutChanged( + //Auth().data().viewLayoutChanged( // #TODO not needed? //) | rpl::map( // [](auto view) { return view->data(); } //), @@ -103,12 +107,10 @@ float64 Float::outRatio() const { } void Float::mouseReleaseEvent(QMouseEvent *e) { - if (_down) { - _down = false; - // #TODO float player - //if (auto media = _item ? _item->getMedia() : nullptr) { - // media->playInline(); - //} + if (base::take(_down) && _item) { + if (const auto controller = _controller->roundVideo(_item)) { + controller->pauseResume(); + } } if (_drag) { finishDrag(outRatio() < 0.5); @@ -125,10 +127,9 @@ void Float::finishDrag(bool closed) { void Float::mouseDoubleClickEvent(QMouseEvent *e) { if (_item) { // Handle second click. - // #TODO float player - //if (auto media = _item->getMedia()) { - // media->playInline(); - //} + if (const auto controller = _controller->roundVideo(_item)) { + controller->pauseResume(); + } Ui::showPeerHistoryAtItem(_item); } } @@ -175,7 +176,8 @@ void Float::paintEvent(QPaintEvent *e) { auto inner = getInnerRect(); p.drawImage(inner.topLeft(), _frame); - auto progress = _roundPlayback ? _roundPlayback->value(getms()) : 1.; + const auto playback = getPlayback(); + const auto progress = playback ? playback->value(getms()) : 1.; if (progress > 0.) { auto pen = st::historyVideoMessageProgressFg->p; auto was = p.pen(); @@ -198,19 +200,31 @@ void Float::paintEvent(QPaintEvent *e) { } Clip::Reader *Float::getReader() const { - // #TODO float player - //if (auto media = _item ? _item->getMedia() : nullptr) { - // if (auto reader = media->getClipReader()) { - // if (reader->started() && reader->mode() == Clip::Reader::Mode::Video) { - // return reader; - // } - // } - //} + if (detached()) { + return nullptr; + } + if (const auto controller = _controller->roundVideo(_item)) { + if (const auto reader = controller->reader()) { + if (reader->started()) { + return reader; + } + } + } + return nullptr; +} + +Clip::Playback *Float::getPlayback() const { + if (detached()) { + return nullptr; + } + if (const auto controller = _controller->roundVideo(_item)) { + return controller->playback(); + } return nullptr; } bool Float::hasFrame() const { - if (auto reader = getReader()) { + if (const auto reader = getReader()) { return !reader->current().isNull(); } return false; @@ -219,14 +233,15 @@ bool Float::hasFrame() const { bool Float::fillFrame() { auto creating = _frame.isNull(); if (creating) { - _frame = QImage(getInnerRect().size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); + _frame = QImage( + getInnerRect().size() * cIntRetinaFactor(), + QImage::Format_ARGB32_Premultiplied); _frame.setDevicePixelRatio(cRetinaFactor()); } - auto frameInner = [this] { - return QRect(0, 0, _frame.width() / cIntRetinaFactor(), _frame.height() / cIntRetinaFactor()); + auto frameInner = [&] { + return QRect(QPoint(), _frame.size() / cIntRetinaFactor()); }; - if (auto reader = getReader()) { - updatePlayback(); + if (const auto reader = getReader()) { auto frame = reader->current(); if (!frame.isNull()) { _frame.fill(Qt::transparent); @@ -249,21 +264,6 @@ bool Float::fillFrame() { return false; } -void Float::updatePlayback() { - if (_item) { - if (!_roundPlayback) { - _roundPlayback = std::make_unique(); - _roundPlayback->setValueChangedCallback([this](float64 value) { - update(); - }); - } - auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Voice); - if (state.id.contextId() == _item->fullId()) { - _roundPlayback->updateState(state); - } - } -} - void Float::repaintItem() { update(); if (hasFrame() && _toggleCallback) { diff --git a/Telegram/SourceFiles/media/player/media_player_float.h b/Telegram/SourceFiles/media/player/media_player_float.h index bf4f082a4..ec5cc1997 100644 --- a/Telegram/SourceFiles/media/player/media_player_float.h +++ b/Telegram/SourceFiles/media/player/media_player_float.h @@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/rp_widget.h" +namespace Window { +class Controller; +} // namespace Window + namespace Media { namespace Clip { class Playback; @@ -18,7 +22,12 @@ namespace Player { class Float : public Ui::RpWidget, private base::Subscriber { public: - Float(QWidget *parent, HistoryItem *item, base::lambda toggleCallback, base::lambda draggedCallback); + Float( + QWidget *parent, + not_null controller, + not_null item, + base::lambda toggleCallback, + base::lambda draggedCallback); HistoryItem *item() const { return _item; @@ -59,14 +68,15 @@ protected: private: float64 outRatio() const; Clip::Reader *getReader() const; + Clip::Playback *getPlayback() const; void repaintItem(); void prepareShadow(); bool hasFrame() const; bool fillFrame(); QRect getInnerRect() const; - void updatePlayback(); void finishDrag(bool closed); + not_null _controller; HistoryItem *_item = nullptr; base::lambda _toggleCallback; @@ -81,8 +91,6 @@ private: QPoint _dragLocalPoint; base::lambda _draggedCallback; - std::unique_ptr _roundPlayback; - }; } // namespace Player diff --git a/Telegram/SourceFiles/media/player/media_player_instance.cpp b/Telegram/SourceFiles/media/player/media_player_instance.cpp index e9cab536d..4bc5d63f7 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.cpp +++ b/Telegram/SourceFiles/media/player/media_player_instance.cpp @@ -10,12 +10,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_document.h" #include "media/media_audio.h" #include "media/media_audio_capture.h" -#include "messenger.h" -#include "auth_session.h" #include "calls/calls_instance.h" #include "history/history.h" #include "history/history_item.h" #include "data/data_media_types.h" +#include "window/window_controller.h" +#include "messenger.h" +#include "mainwindow.h" +#include "auth_session.h" namespace Media { namespace Player { @@ -230,13 +232,15 @@ bool Instance::moveInPlaylist( item->fullId() }); } - if (document->isAudioFile()) { + if (document->isAudioFile() + || document->isVoiceMessage() + || document->isVideoMessage()) { play(AudioMsgId(document, item->fullId())); } else { - DocumentOpenClickHandler::doOpen( - document, - item, - ActionOnLoadPlayInline); + //DocumentOpenClickHandler::doOpen( + // document, + // item, + // ActionOnLoadPlayInline); } return true; } @@ -300,12 +304,9 @@ void Instance::play(const AudioMsgId &audioId) { documentLoadProgress(document); } } else if (document->isVideoMessage()) { - // #TODO float player - //if (const auto item = App::histItemById(audioId.contextId())) { - // if (const auto media = item->getMedia()) { - // media->playInline(); - // } - //} + if (const auto item = App::histItemById(audioId.contextId())) { + App::wnd()->controller()->startRoundVideo(item); + } } } diff --git a/Telegram/SourceFiles/media/player/media_player_round_controller.cpp b/Telegram/SourceFiles/media/player/media_player_round_controller.cpp new file mode 100644 index 000000000..b0c07fb5a --- /dev/null +++ b/Telegram/SourceFiles/media/player/media_player_round_controller.cpp @@ -0,0 +1,179 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "media/player/media_player_round_controller.h" + +#include "media/media_clip_reader.h" +#include "media/media_audio.h" +#include "media/player/media_player_instance.h" +#include "media/view/media_clip_playback.h" +#include "history/history_item.h" +#include "window/window_controller.h" +#include "data/data_media_types.h" +#include "data/data_document.h" +#include "data/data_session.h" +#include "auth_session.h" + +namespace Media { +namespace Player { + +struct RoundController::CreateTag { +}; + +std::unique_ptr RoundController::TryStart( + not_null parent, + not_null item) { + const auto media = item->media(); + if (!media) { + return nullptr; + } + const auto document = media->document(); + if (!document || !document->isVideoMessage()) { + return nullptr; + } + return std::make_unique(CreateTag(), parent, item); +} + +RoundController::RoundController( + CreateTag&&, + not_null parent, + not_null item) +: _parent(parent) +, _data(item->media()->document()) +, _context(item) { + Expects(_data->isVideoMessage()); + + subscribe(instance()->updatedNotifier(), [this](const TrackState &state) { + handleAudioUpdate(state); + }); + + _reader = Clip::MakeReader( + _data, + _context->fullId(), + [=](Clip::Notification notification) { callback(notification); }, + Clip::Reader::Mode::Video); + _playback = std::make_unique(); + _playback->setValueChangedCallback([=](float64 value) { + Auth().data().requestItemRepaint(_context); + }); + Auth().data().markMediaRead(_data); + Auth().data().itemRemoved( + ) | rpl::start_with_next([=](not_null item) { + if (item == _context) { + stop(State::Stopped); + } + }, lifetime()); + Auth().data().itemRepaintRequest( + ) | rpl::start_with_next([=](not_null item) { + if (item == _context) { + checkReaderState(); + } + }, lifetime()); +} + +rpl::lifetime &RoundController::lifetime() { + return _lifetime; +} + +FullMsgId RoundController::contextId() const { + return _context->fullId(); +} + +void RoundController::pauseResume() { + if (checkReaderState()) { + _reader->pauseResumeVideo(); + } +} + +Clip::Reader *RoundController::reader() const { + return _reader ? _reader.get() : nullptr; +} + +Clip::Playback *RoundController::playback() const { + return _playback.get(); +} + +void RoundController::handleAudioUpdate(const TrackState &state) { + if (state.id.type() != AudioMsgId::Type::Voice) { + return; + } + const auto audio = _reader->audioMsgId(); + const auto another = (state.id != _reader->audioMsgId()); + const auto stopped = IsStoppedOrStopping(state.state); + if ((another && !stopped) || (!another && stopped)) { + stop(State::Stopped); + return; + } else if (another) { + return; + } + if (_playback) { + _playback->updateState(state); + } + if (IsPaused(state.state) || state.state == State::Pausing) { + if (!_reader->videoPaused()) { + _reader->pauseResumeVideo(); + } + } else { + if (_reader->videoPaused()) { + _reader->pauseResumeVideo(); + } + } +} + +void RoundController::callback(Clip::Notification notification) { + if (!_reader) { + return; + } + switch (notification) { + case Clip::NotificationReinit: { + if (checkReaderState()) { + Auth().data().requestItemResize(_context); + } + } break; + + case Clip::NotificationRepaint: { + Auth().data().requestItemRepaint(_context); + } break; + } +} + +bool RoundController::checkReaderState() { + if (!_reader) { + return false; + } + const auto state = _reader->state(); + if (state == Media::Clip::State::Error) { + stop(State::StoppedAtError); + return false; + } else if (state == Media::Clip::State::Finished) { + stop(State::StoppedAtEnd); + return false; + } else if (_reader->ready() && !_reader->started()) { + const auto size = QSize(_reader->width(), _reader->height()) + / cIntRetinaFactor(); + _reader->start( + size.width(), + size.height(), + size.width(), + size.height(), + ImageRoundRadius::Ellipse, + RectPart::AllCorners); + } + return true; +} + +void RoundController::stop(State state) { + if (const auto audioId = _reader->audioMsgId()) { + mixer()->stop(audioId, state); + } + _parent->roundVideoFinished(this); +} + +RoundController::~RoundController() = default; + +} // namespace Player +} // namespace Media diff --git a/Telegram/SourceFiles/media/player/media_player_round_controller.h b/Telegram/SourceFiles/media/player/media_player_round_controller.h new file mode 100644 index 000000000..303db6365 --- /dev/null +++ b/Telegram/SourceFiles/media/player/media_player_round_controller.h @@ -0,0 +1,67 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +class HistoryItem; +class AudioMsgId; + +namespace Window { +class Controller; +} // namespace Window + +namespace Media { +namespace Clip { +class Playback; +} // namespace Clip +} // namespace Media + +namespace Media { +namespace Player { + +struct TrackState; +enum class State; + +class RoundController : private base::Subscriber { + struct CreateTag; + +public: + static std::unique_ptr TryStart( + not_null parent, + not_null item); + + FullMsgId contextId() const; + void pauseResume(); + Clip::Reader *reader() const; + Clip::Playback *playback() const; + + rpl::lifetime &lifetime(); + + RoundController( + CreateTag&&, + not_null parent, + not_null item); + ~RoundController(); + +private: + void stop(State state); + bool checkReaderState(); + void callback(Clip::Notification notification); + void handleAudioUpdate(const TrackState &audioId); + + not_null _parent; + not_null _data; + not_null _context; + Clip::ReaderPointer _reader; + std::unique_ptr _playback; + + rpl::lifetime _lifetime; + +}; + +} // namespace Player +} // namespace Media diff --git a/Telegram/SourceFiles/window/window_controller.cpp b/Telegram/SourceFiles/window/window_controller.cpp index a57544868..8ef31edb5 100644 --- a/Telegram/SourceFiles/window/window_controller.cpp +++ b/Telegram/SourceFiles/window/window_controller.cpp @@ -12,16 +12,30 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "history/history_item.h" #include "history/view/history_view_element.h" +#include "media/player/media_player_round_controller.h" +#include "data/data_session.h" +#include "boxes/calendar_box.h" #include "mainwidget.h" #include "mainwindow.h" -#include "styles/style_window.h" -#include "styles/style_dialogs.h" -#include "boxes/calendar_box.h" #include "auth_session.h" #include "apiwrap.h" +#include "styles/style_window.h" +#include "styles/style_dialogs.h" namespace Window { +Controller::Controller(not_null window) +: _window(window) { + Auth().data().animationPlayInlineRequest( + ) | rpl::start_with_next([this](auto item) { + if (const auto video = roundVideo(item)) { + video->pauseResume(); + } else { + startRoundVideo(item); + } + }, lifetime()); +} + void Controller::enableGifPauseReason(GifPauseReason reason) { if (!(_gifPauseReasons & reason)) { auto notify = (static_cast(_gifPauseReasons) < static_cast(reason)); @@ -405,4 +419,40 @@ not_null Controller::chats() const { return App::wnd()->chatsWidget(); } +bool Controller::startRoundVideo(not_null context) { + if (auto video = RoundController::TryStart(this, context)) { + enableGifPauseReason(Window::GifPauseReason::RoundPlaying); + _roundVideo = std::move(video); + return true; + } + return false; +} + +auto Controller::currentRoundVideo() const -> RoundController* { + return _roundVideo.get(); +} + +auto Controller::roundVideo(not_null context) const +-> RoundController* { + return roundVideo(context->fullId()); +} + +auto Controller::roundVideo(FullMsgId contextId) const -> RoundController* { + if (const auto result = currentRoundVideo()) { + if (result->contextId() == contextId) { + return result; + } + } + return nullptr; +} + +void Controller::roundVideoFinished(not_null video) { + if (video == _roundVideo.get()) { + _roundVideo = nullptr; + disableGifPauseReason(Window::GifPauseReason::RoundPlaying); + } +} + +Controller::~Controller() = default; + } // namespace Window diff --git a/Telegram/SourceFiles/window/window_controller.h b/Telegram/SourceFiles/window/window_controller.h index 13aeb7f4a..e9d9c4207 100644 --- a/Telegram/SourceFiles/window/window_controller.h +++ b/Telegram/SourceFiles/window/window_controller.h @@ -14,6 +14,12 @@ class MainWidget; class HistoryMessage; class HistoryService; +namespace Media { +namespace Player { +class RoundController; +} // namespace Player +} // namespace Media + namespace Window { class LayerWidget; @@ -89,12 +95,13 @@ public: not_null history, const SectionShow ¶ms = SectionShow()); + virtual ~Navigation() = default; + }; class Controller : public Navigation { public: - Controller(not_null window) : _window(window) { - } + Controller(not_null window); not_null window() const { return _window; @@ -197,6 +204,19 @@ public: return this; } + using RoundController = Media::Player::RoundController; + bool startRoundVideo(not_null context); + RoundController *currentRoundVideo() const; + RoundController *roundVideo(not_null context) const; + RoundController *roundVideo(FullMsgId contextId) const; + void roundVideoFinished(not_null video); + + rpl::lifetime &lifetime() { + return _lifetime; + } + + ~Controller(); + private: int minimalThreeColumnWidth() const; not_null chats() const; @@ -220,6 +240,10 @@ private: base::Variable _dialogsListFocused = { false }; base::Variable _dialogsListDisplayForced = { false }; + std::unique_ptr _roundVideo; + + rpl::lifetime _lifetime; + }; } // namespace Window diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index 39fe5908a..f1f1ed941 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -360,6 +360,8 @@ <(src_loc)/media/player/media_player_instance.h <(src_loc)/media/player/media_player_panel.cpp <(src_loc)/media/player/media_player_panel.h +<(src_loc)/media/player/media_player_round_controller.cpp +<(src_loc)/media/player/media_player_round_controller.h <(src_loc)/media/player/media_player_volume_controller.cpp <(src_loc)/media/player/media_player_volume_controller.h <(src_loc)/media/player/media_player_widget.cpp