mirror of
https://github.com/vale981/tdesktop
synced 2025-03-06 10:11:41 -05:00
Fix voice/video messages in chats/feed.
This commit is contained in:
parent
f9154c4ed0
commit
861ab85ca1
30 changed files with 730 additions and 394 deletions
|
@ -18,7 +18,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
#include "history/history_media_types.h"
|
#include "history/history_media_types.h"
|
||||||
|
#include "window/window_controller.h"
|
||||||
#include "auth_session.h"
|
#include "auth_session.h"
|
||||||
|
#include "mainwindow.h"
|
||||||
#include "messenger.h"
|
#include "messenger.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -286,14 +288,14 @@ void DocumentOpenClickHandler::doOpen(
|
||||||
} else if (data->size < App::kImageSizeLimit) {
|
} else if (data->size < App::kImageSizeLimit) {
|
||||||
if (!data->data().isEmpty() && playAnimation) {
|
if (!data->data().isEmpty() && playAnimation) {
|
||||||
if (action == ActionOnLoadPlayInline && context) {
|
if (action == ActionOnLoadPlayInline && context) {
|
||||||
Auth().data().requestItemPlayInline(context);
|
Auth().data().requestAnimationPlayInline(context);
|
||||||
} else {
|
} else {
|
||||||
Messenger::Instance().showDocument(data, context);
|
Messenger::Instance().showDocument(data, context);
|
||||||
}
|
}
|
||||||
} else if (location.accessEnable()) {
|
} else if (location.accessEnable()) {
|
||||||
if (data->isAnimation() || QImageReader(location.name()).canRead()) {
|
if (playAnimation || QImageReader(location.name()).canRead()) {
|
||||||
if (action == ActionOnLoadPlayInline && context) {
|
if (playAnimation && action == ActionOnLoadPlayInline && context) {
|
||||||
Auth().data().requestItemPlayInline(context);
|
Auth().data().requestAnimationPlayInline(context);
|
||||||
} else {
|
} else {
|
||||||
Messenger::Instance().showDocument(data, context);
|
Messenger::Instance().showDocument(data, context);
|
||||||
}
|
}
|
||||||
|
@ -592,7 +594,7 @@ void DocumentData::performActionOnLoad() {
|
||||||
} else if (playAnimation) {
|
} else if (playAnimation) {
|
||||||
if (loaded()) {
|
if (loaded()) {
|
||||||
if (_actionOnLoad == ActionOnLoadPlayInline && item) {
|
if (_actionOnLoad == ActionOnLoadPlayInline && item) {
|
||||||
Auth().data().requestItemPlayInline(item);
|
Auth().data().requestAnimationPlayInline(item);
|
||||||
} else {
|
} else {
|
||||||
Messenger::Instance().showDocument(this, item);
|
Messenger::Instance().showDocument(this, item);
|
||||||
}
|
}
|
||||||
|
@ -610,11 +612,7 @@ void DocumentData::performActionOnLoad() {
|
||||||
Auth().data().markMediaRead(this);
|
Auth().data().markMediaRead(this);
|
||||||
} else if (loc.accessEnable()) {
|
} else if (loc.accessEnable()) {
|
||||||
if (showImage && QImageReader(loc.name()).canRead()) {
|
if (showImage && QImageReader(loc.name()).canRead()) {
|
||||||
if (_actionOnLoad == ActionOnLoadPlayInline && item) {
|
Messenger::Instance().showDocument(this, item);
|
||||||
Auth().data().requestItemPlayInline(item);
|
|
||||||
} else {
|
|
||||||
Messenger::Instance().showDocument(this, item);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
File::Launch(already);
|
File::Launch(already);
|
||||||
}
|
}
|
||||||
|
|
|
@ -208,6 +208,7 @@ rpl::producer<not_null<const HistoryItem*>> Session::itemResizeRequest() const {
|
||||||
void Session::requestViewResize(not_null<ViewElement*> view) {
|
void Session::requestViewResize(not_null<ViewElement*> view) {
|
||||||
view->setPendingResize();
|
view->setPendingResize();
|
||||||
_viewResizeRequest.fire_copy(view);
|
_viewResizeRequest.fire_copy(view);
|
||||||
|
notifyViewLayoutChange(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<not_null<ViewElement*>> Session::viewResizeRequest() const {
|
rpl::producer<not_null<ViewElement*>> Session::viewResizeRequest() const {
|
||||||
|
@ -225,12 +226,12 @@ rpl::producer<not_null<HistoryItem*>> Session::itemViewRefreshRequest() const {
|
||||||
return _itemViewRefreshRequest.events();
|
return _itemViewRefreshRequest.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::requestItemPlayInline(not_null<const HistoryItem*> item) {
|
void Session::requestAnimationPlayInline(not_null<HistoryItem*> item) {
|
||||||
_itemPlayInlineRequest.fire_copy(item);
|
_animationPlayInlineRequest.fire_copy(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<not_null<const HistoryItem*>> Session::itemPlayInlineRequest() const {
|
rpl::producer<not_null<HistoryItem*>> Session::animationPlayInlineRequest() const {
|
||||||
return _itemPlayInlineRequest.events();
|
return _animationPlayInlineRequest.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::notifyItemRemoved(not_null<const HistoryItem*> item) {
|
void Session::notifyItemRemoved(not_null<const HistoryItem*> item) {
|
||||||
|
@ -1326,9 +1327,7 @@ void Session::unregisterAutoplayAnimation(
|
||||||
void Session::stopAutoplayAnimations() {
|
void Session::stopAutoplayAnimations() {
|
||||||
for (const auto [reader, view] : base::take(_autoplayAnimations)) {
|
for (const auto [reader, view] : base::take(_autoplayAnimations)) {
|
||||||
if (const auto media = view->media()) {
|
if (const auto media = view->media()) {
|
||||||
if (!media->isRoundVideoPlaying()) {
|
media->stopAnimation();
|
||||||
media->stopInline();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,8 +72,8 @@ public:
|
||||||
rpl::producer<not_null<ViewElement*>> viewResizeRequest() const;
|
rpl::producer<not_null<ViewElement*>> viewResizeRequest() const;
|
||||||
void requestItemViewRefresh(not_null<HistoryItem*> item);
|
void requestItemViewRefresh(not_null<HistoryItem*> item);
|
||||||
rpl::producer<not_null<HistoryItem*>> itemViewRefreshRequest() const;
|
rpl::producer<not_null<HistoryItem*>> itemViewRefreshRequest() const;
|
||||||
void requestItemPlayInline(not_null<const HistoryItem*> item);
|
void requestAnimationPlayInline(not_null<HistoryItem*> item);
|
||||||
rpl::producer<not_null<const HistoryItem*>> itemPlayInlineRequest() const;
|
rpl::producer<not_null<HistoryItem*>> animationPlayInlineRequest() const;
|
||||||
void notifyHistoryUnloaded(not_null<const History*> history);
|
void notifyHistoryUnloaded(not_null<const History*> history);
|
||||||
rpl::producer<not_null<const History*>> historyUnloaded() const;
|
rpl::producer<not_null<const History*>> historyUnloaded() const;
|
||||||
|
|
||||||
|
@ -445,7 +445,7 @@ private:
|
||||||
rpl::event_stream<not_null<const HistoryItem*>> _itemResizeRequest;
|
rpl::event_stream<not_null<const HistoryItem*>> _itemResizeRequest;
|
||||||
rpl::event_stream<not_null<ViewElement*>> _viewResizeRequest;
|
rpl::event_stream<not_null<ViewElement*>> _viewResizeRequest;
|
||||||
rpl::event_stream<not_null<HistoryItem*>> _itemViewRefreshRequest;
|
rpl::event_stream<not_null<HistoryItem*>> _itemViewRefreshRequest;
|
||||||
rpl::event_stream<not_null<const HistoryItem*>> _itemPlayInlineRequest;
|
rpl::event_stream<not_null<HistoryItem*>> _animationPlayInlineRequest;
|
||||||
rpl::event_stream<not_null<const HistoryItem*>> _itemRemoved;
|
rpl::event_stream<not_null<const HistoryItem*>> _itemRemoved;
|
||||||
rpl::event_stream<not_null<const History*>> _historyUnloaded;
|
rpl::event_stream<not_null<const History*>> _historyUnloaded;
|
||||||
rpl::event_stream<not_null<const History*>> _historyCleared;
|
rpl::event_stream<not_null<const History*>> _historyCleared;
|
||||||
|
|
|
@ -239,16 +239,6 @@ bool isLayerShown() {
|
||||||
return false;
|
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) {
|
void showPeerProfile(const PeerId &peer) {
|
||||||
if (auto window = App::wnd()) {
|
if (auto window = App::wnd()) {
|
||||||
if (auto controller = window->controller()) {
|
if (auto controller = window->controller()) {
|
||||||
|
@ -340,33 +330,6 @@ void historyMuteUpdated(History *history) {
|
||||||
if (MainWidget *m = App::main()) m->notify_historyMuteUpdated(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() {
|
void unreadCounterUpdated() {
|
||||||
Global::RefHandleUnreadCounterUpdate().call();
|
Global::RefHandleUnreadCounterUpdate().call();
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,8 +182,6 @@ void hideLayer(anim::type animated = anim::type::normal);
|
||||||
void hideSettingsAndLayer(anim::type animated = anim::type::normal);
|
void hideSettingsAndLayer(anim::type animated = anim::type::normal);
|
||||||
bool isLayerShown();
|
bool isLayerShown();
|
||||||
|
|
||||||
void autoplayMediaInlineAsync(const FullMsgId &msgId);
|
|
||||||
|
|
||||||
void showPeerProfile(const PeerId &peer);
|
void showPeerProfile(const PeerId &peer);
|
||||||
inline void showPeerProfile(const PeerData *peer) {
|
inline void showPeerProfile(const PeerData *peer) {
|
||||||
showPeerProfile(peer->id);
|
showPeerProfile(peer->id);
|
||||||
|
|
|
@ -231,11 +231,11 @@ InnerWidget::InnerWidget(
|
||||||
refreshItem(view);
|
refreshItem(view);
|
||||||
}
|
}
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
Auth().data().itemPlayInlineRequest(
|
Auth().data().animationPlayInlineRequest(
|
||||||
) | rpl::start_with_next([this](auto item) {
|
) | rpl::start_with_next([this](auto item) {
|
||||||
if (const auto view = viewForItem(item)) {
|
if (const auto view = viewForItem(item)) {
|
||||||
if (const auto media = view->media()) {
|
if (const auto media = view->media()) {
|
||||||
media->playInline(true);
|
media->playAnimation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
@ -474,6 +474,20 @@ std::unique_ptr<HistoryView::Element> InnerWidget::elementCreate(
|
||||||
return std::make_unique<HistoryView::Service>(this, message);
|
return std::make_unique<HistoryView::Service>(this, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InnerWidget::elementAnimationAutoplayAsync(
|
||||||
|
not_null<const HistoryView::Element*> 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<SectionMemento*> memento) {
|
void InnerWidget::saveState(not_null<SectionMemento*> memento) {
|
||||||
memento->setFilter(std::move(_filter));
|
memento->setFilter(std::move(_filter));
|
||||||
memento->setAdmins(std::move(_admins));
|
memento->setAdmins(std::move(_admins));
|
||||||
|
|
|
@ -74,6 +74,8 @@ public:
|
||||||
not_null<HistoryMessage*> message) override;
|
not_null<HistoryMessage*> message) override;
|
||||||
std::unique_ptr<HistoryView::Element> elementCreate(
|
std::unique_ptr<HistoryView::Element> elementCreate(
|
||||||
not_null<HistoryService*> message) override;
|
not_null<HistoryService*> message) override;
|
||||||
|
void elementAnimationAutoplayAsync(
|
||||||
|
not_null<const HistoryView::Element*> element) override;
|
||||||
|
|
||||||
~InnerWidget();
|
~InnerWidget();
|
||||||
|
|
||||||
|
|
|
@ -164,20 +164,6 @@ rpl::producer<Data::MessagesSlice> Widget::listSource(
|
||||||
limitAfter);
|
limitAfter);
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryView::Context Widget::elementContext() {
|
|
||||||
return HistoryView::Context::Feed;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<HistoryView::Element> Widget::elementCreate(
|
|
||||||
not_null<HistoryMessage*> message) {
|
|
||||||
return std::make_unique<HistoryView::Message>(this, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<HistoryView::Element> Widget::elementCreate(
|
|
||||||
not_null<HistoryService*> message) {
|
|
||||||
return std::make_unique<HistoryView::Service>(this, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<Window::SectionMemento> Widget::createMemento() {
|
std::unique_ptr<Window::SectionMemento> Widget::createMemento() {
|
||||||
auto result = std::make_unique<Memento>(_feed);
|
auto result = std::make_unique<Memento>(_feed);
|
||||||
saveState(result.get());
|
saveState(result.get());
|
||||||
|
|
|
@ -67,13 +67,6 @@ public:
|
||||||
int limitBefore,
|
int limitBefore,
|
||||||
int limitAfter) override;
|
int limitAfter) override;
|
||||||
|
|
||||||
// HistoryView::ElementDelegate interface.
|
|
||||||
HistoryView::Context elementContext() override;
|
|
||||||
std::unique_ptr<HistoryView::Element> elementCreate(
|
|
||||||
not_null<HistoryMessage*> message) override;
|
|
||||||
std::unique_ptr<HistoryView::Element> elementCreate(
|
|
||||||
not_null<HistoryService*> message) override;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void resizeEvent(QResizeEvent *e) override;
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
void paintEvent(QPaintEvent *e) override;
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
|
|
@ -2922,6 +2922,18 @@ not_null<HistoryView::ElementDelegate*> HistoryInner::ElementDelegate() {
|
||||||
not_null<HistoryService*> message) override {
|
not_null<HistoryService*> message) override {
|
||||||
return std::make_unique<HistoryView::Service>(this, message);
|
return std::make_unique<HistoryView::Service>(this, message);
|
||||||
}
|
}
|
||||||
|
void elementAnimationAutoplayAsync(
|
||||||
|
not_null<const HistoryView::Element*> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -639,33 +639,6 @@ bool HistoryItem::isEmpty() const {
|
||||||
&& !Has<HistoryMessageLogEntryOriginal>();
|
&& !Has<HistoryMessageLogEntryOriginal>();
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
HistoryItem *HistoryItem::previousItem() const {
|
||||||
if (const auto view = mainView()) {
|
if (const auto view = mainView()) {
|
||||||
if (const auto previous = view->previousInBlocks()) {
|
if (const auto previous = view->previousInBlocks()) {
|
||||||
|
|
|
@ -266,8 +266,6 @@ public:
|
||||||
|
|
||||||
MessageGroupId groupId() const;
|
MessageGroupId groupId() const;
|
||||||
|
|
||||||
void audioTrackUpdated();
|
|
||||||
|
|
||||||
HistoryItem *previousItem() const;
|
HistoryItem *previousItem() const;
|
||||||
HistoryItem *nextItem() const;
|
HistoryItem *nextItem() const;
|
||||||
|
|
||||||
|
|
|
@ -130,20 +130,14 @@ public:
|
||||||
virtual DocumentData *getDocument() const {
|
virtual DocumentData *getDocument() const {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
virtual Media::Clip::Reader *getClipReader() {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool playInline(/*bool autoplay = false*/) {
|
void playAnimation() {
|
||||||
return playInline(false);
|
playAnimation(false);
|
||||||
}
|
}
|
||||||
virtual bool playInline(bool autoplay) {
|
void autoplayAnimation() {
|
||||||
return false;
|
playAnimation(true);
|
||||||
}
|
}
|
||||||
virtual void stopInline() {
|
virtual void stopAnimation() {
|
||||||
}
|
|
||||||
virtual bool isRoundVideoPlaying() const {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual QSize sizeForGrouping() const {
|
virtual QSize sizeForGrouping() const {
|
||||||
|
@ -243,6 +237,9 @@ protected:
|
||||||
QSize countCurrentSize(int newWidth) override;
|
QSize countCurrentSize(int newWidth) override;
|
||||||
Text createCaption(not_null<HistoryItem*> item) const;
|
Text createCaption(not_null<HistoryItem*> item) const;
|
||||||
|
|
||||||
|
virtual void playAnimation(bool autoplay) {
|
||||||
|
}
|
||||||
|
|
||||||
not_null<Element*> _parent;
|
not_null<Element*> _parent;
|
||||||
MediaInBubbleState _inBubbleState = MediaInBubbleState::None;
|
MediaInBubbleState _inBubbleState = MediaInBubbleState::None;
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "media/media_audio.h"
|
#include "media/media_audio.h"
|
||||||
#include "media/media_clip_reader.h"
|
#include "media/media_clip_reader.h"
|
||||||
#include "media/player/media_player_instance.h"
|
#include "media/player/media_player_instance.h"
|
||||||
|
#include "media/player/media_player_round_controller.h"
|
||||||
#include "media/view/media_clip_playback.h"
|
#include "media/view/media_clip_playback.h"
|
||||||
#include "boxes/confirm_box.h"
|
#include "boxes/confirm_box.h"
|
||||||
#include "boxes/add_contact_box.h"
|
#include "boxes/add_contact_box.h"
|
||||||
|
@ -1918,17 +1919,6 @@ void HistoryDocument::refreshParentId(not_null<HistoryItem*> 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 {
|
TextWithEntities HistoryDocument::getCaption() const {
|
||||||
if (const auto captioned = Get<HistoryDocumentCaptioned>()) {
|
if (const auto captioned = Get<HistoryDocumentCaptioned>()) {
|
||||||
return captioned->_caption.originalTextWithEntities();
|
return captioned->_caption.originalTextWithEntities();
|
||||||
|
@ -1979,9 +1969,10 @@ QSize HistoryGif::countOptimalSize() {
|
||||||
setClipReader(Media::Clip::ReaderPointer::Bad());
|
setClipReader(Media::Clip::ReaderPointer::Bad());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_gif && _gif->ready()) {
|
const auto reader = currentReader();
|
||||||
tw = convertScale(_gif->width());
|
if (reader) {
|
||||||
th = convertScale(_gif->height());
|
tw = convertScale(reader->width());
|
||||||
|
th = convertScale(reader->height());
|
||||||
} else {
|
} else {
|
||||||
tw = convertScale(_data->dimensions.width()), th = convertScale(_data->dimensions.height());
|
tw = convertScale(_data->dimensions.width()), th = convertScale(_data->dimensions.height());
|
||||||
if (!tw || !th) {
|
if (!tw || !th) {
|
||||||
|
@ -2005,7 +1996,7 @@ QSize HistoryGif::countOptimalSize() {
|
||||||
auto maxWidth = qMax(tw, st::minPhotoSize);
|
auto maxWidth = qMax(tw, st::minPhotoSize);
|
||||||
auto minHeight = qMax(th, st::minPhotoSize);
|
auto minHeight = qMax(th, st::minPhotoSize);
|
||||||
accumulate_max(maxWidth, _parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
|
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()));
|
accumulate_max(maxWidth, gifMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
|
||||||
}
|
}
|
||||||
if (_parent->hasBubble()) {
|
if (_parent->hasBubble()) {
|
||||||
|
@ -2033,9 +2024,10 @@ QSize HistoryGif::countCurrentSize(int newWidth) {
|
||||||
auto availableWidth = newWidth;
|
auto availableWidth = newWidth;
|
||||||
|
|
||||||
int tw = 0, th = 0;
|
int tw = 0, th = 0;
|
||||||
if (_gif && _gif->ready()) {
|
const auto reader = currentReader();
|
||||||
tw = convertScale(_gif->width());
|
if (reader) {
|
||||||
th = convertScale(_gif->height());
|
tw = convertScale(reader->width());
|
||||||
|
th = convertScale(reader->height());
|
||||||
} else {
|
} else {
|
||||||
tw = convertScale(_data->dimensions.width()), th = convertScale(_data->dimensions.height());
|
tw = convertScale(_data->dimensions.width()), th = convertScale(_data->dimensions.height());
|
||||||
if (!tw || !th) {
|
if (!tw || !th) {
|
||||||
|
@ -2065,14 +2057,31 @@ QSize HistoryGif::countCurrentSize(int newWidth) {
|
||||||
newWidth = qMax(tw, st::minPhotoSize);
|
newWidth = qMax(tw, st::minPhotoSize);
|
||||||
auto newHeight = qMax(th, st::minPhotoSize);
|
auto newHeight = qMax(th, st::minPhotoSize);
|
||||||
accumulate_max(newWidth, _parent->infoWidth() + 2 * st::msgDateImgDelta + st::msgDateImgPadding.x());
|
accumulate_max(newWidth, _parent->infoWidth() + 2 * st::msgDateImgDelta + st::msgDateImgPadding.x());
|
||||||
if (_gif && _gif->ready()) {
|
if (reader) {
|
||||||
if (!_gif->started()) {
|
const auto own = (reader->mode() == Media::Clip::Reader::Mode::Gif);
|
||||||
|
if (own && !reader->started()) {
|
||||||
auto isRound = _data->isVideoMessage();
|
auto isRound = _data->isVideoMessage();
|
||||||
auto inWebPage = (_parent->media() != this);
|
auto inWebPage = (_parent->media() != this);
|
||||||
auto roundRadius = isRound ? ImageRoundRadius::Ellipse : inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large;
|
auto roundRadius = isRound
|
||||||
auto roundCorners = (isRound || inWebPage) ? RectPart::AllCorners : ((isBubbleTop() ? (RectPart::TopLeft | RectPart::TopRight) : RectPart::None)
|
? ImageRoundRadius::Ellipse
|
||||||
| ((isBubbleBottom() && _caption.isEmpty()) ? (RectPart::BottomLeft | RectPart::BottomRight) : RectPart::None));
|
: inWebPage
|
||||||
_gif->start(_thumbw, _thumbh, newWidth, newHeight, roundRadius, roundCorners);
|
? 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 {
|
} else {
|
||||||
accumulate_max(newWidth, gifMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
|
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 displayLoading = (item->id < 0) || _data->displayLoading();
|
||||||
auto selected = (selection == FullSelection);
|
auto selected = (selection == FullSelection);
|
||||||
|
|
||||||
auto videoFinished = _gif
|
if (loaded
|
||||||
&& (_gif->mode() == Media::Clip::Reader::Mode::Video)
|
&& cAutoPlayGif()
|
||||||
&& (_gif->state() == Media::Clip::State::Finished);
|
&& !_gif
|
||||||
if (loaded && cAutoPlayGif() && ((!_gif && !_gif.isBad()) || videoFinished)) {
|
&& !_gif.isBad()
|
||||||
Ui::autoplayMediaInlineAsync(item->fullId());
|
&& !activeRoundVideo()) {
|
||||||
|
_parent->delegate()->elementAnimationAutoplayAsync(_parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto paintx = 0, painty = 0, paintw = width(), painth = height();
|
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 isRound = _data->isVideoMessage();
|
||||||
auto displayMute = false;
|
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 (!animating || item->id < 0) {
|
||||||
if (displayLoading) {
|
if (displayLoading) {
|
||||||
|
@ -2143,7 +2157,7 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateStatusText();
|
updateStatusText();
|
||||||
} else if (_gif && _gif->mode() == Media::Clip::Reader::Mode::Video) {
|
} else if (playingVideo) {
|
||||||
updateStatusText();
|
updateStatusText();
|
||||||
}
|
}
|
||||||
auto radial = isRadialAnimation(ms);
|
auto radial = isRadialAnimation(ms);
|
||||||
|
@ -2180,18 +2194,16 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM
|
||||||
if (animating) {
|
if (animating) {
|
||||||
auto paused = App::wnd()->controller()->isGifPausedAtLeastFor(Window::GifPauseReason::Any);
|
auto paused = App::wnd()->controller()->isGifPausedAtLeastFor(Window::GifPauseReason::Any);
|
||||||
if (isRound) {
|
if (isRound) {
|
||||||
if (_gif->mode() == Media::Clip::Reader::Mode::Video) {
|
if (playingVideo) {
|
||||||
paused = false;
|
paused = false;
|
||||||
} else {
|
} else {
|
||||||
displayMute = true;
|
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) {
|
if (const auto playback = videoPlayback()) {
|
||||||
_roundPlayback.reset();
|
const auto value = playback->value(ms);
|
||||||
} else if (_roundPlayback) {
|
|
||||||
auto value = _roundPlayback->value(ms);
|
|
||||||
if (value > 0.) {
|
if (value > 0.) {
|
||||||
auto pen = st::historyVideoMessageProgressFg->p;
|
auto pen = st::historyVideoMessageProgressFg->p;
|
||||||
auto was = p.pen();
|
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);
|
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 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);
|
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);
|
p.setPen(Qt::NoPen);
|
||||||
|
@ -2594,23 +2606,19 @@ void HistoryGif::updateStatusText() const {
|
||||||
statusSize = _data->loadOffset();
|
statusSize = _data->loadOffset();
|
||||||
} else if (_data->loaded()) {
|
} else if (_data->loaded()) {
|
||||||
statusSize = FileStatusSizeLoaded;
|
statusSize = FileStatusSizeLoaded;
|
||||||
if (_gif && _gif->mode() == Media::Clip::Reader::Mode::Video) {
|
if (const auto video = activeRoundPlayer()) {
|
||||||
statusSize = -1 - _data->duration();
|
statusSize = -1 - _data->duration();
|
||||||
|
|
||||||
auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Voice);
|
const auto state = Media::Player::mixer()->currentState(
|
||||||
if (state.id == _gif->audioMsgId()) {
|
AudioMsgId::Type::Voice);
|
||||||
if (state.length) {
|
if (state.id == video->audioMsgId() && state.length) {
|
||||||
auto position = int64(0);
|
auto position = int64(0);
|
||||||
if (Media::Player::IsStoppedAtEnd(state.state)) {
|
if (Media::Player::IsStoppedAtEnd(state.state)) {
|
||||||
position = state.length;
|
position = state.length;
|
||||||
} else if (!Media::Player::IsStoppedOrStopping(state.state)) {
|
} else if (!Media::Player::IsStoppedOrStopping(state.state)) {
|
||||||
position = state.position;
|
position = state.position;
|
||||||
}
|
|
||||||
accumulate_max(statusSize, -1 - int((state.length - position) / state.frequency + 1));
|
|
||||||
}
|
|
||||||
if (_roundPlayback) {
|
|
||||||
_roundPlayback->updateState(state);
|
|
||||||
}
|
}
|
||||||
|
accumulate_max(statusSize, -1 - int((state.length - position) / state.frequency + 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -2646,63 +2654,96 @@ int HistoryGif::additionalWidth(const HistoryMessageVia *via, const HistoryMessa
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HistoryGif::playInline(bool autoplay) {
|
Media::Player::RoundController *HistoryGif::activeRoundVideo() const {
|
||||||
using Mode = Media::Clip::Reader::Mode;
|
return App::wnd()->controller()->roundVideo(_parent->data());
|
||||||
if (_data->isVideoMessage() && _gif) {
|
}
|
||||||
// Stop autoplayed silent video when we start playback by click.
|
|
||||||
// Stop finished video message when autoplay starts.
|
Media::Clip::Reader *HistoryGif::activeRoundPlayer() const {
|
||||||
if (!autoplay) {
|
if (const auto video = activeRoundVideo()) {
|
||||||
if (_gif->mode() == Mode::Gif) {
|
if (const auto result = video->reader()) {
|
||||||
stopInline();
|
if (result->ready()) {
|
||||||
} else {
|
return result;
|
||||||
_gif->pauseResumeVideo();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
} 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) {
|
if (_gif) {
|
||||||
stopInline();
|
stopAnimation();
|
||||||
} else if (_data->loaded(DocumentData::FilePathResolveChecked)) {
|
} else if (_data->loaded(DocumentData::FilePathResolveChecked)) {
|
||||||
if (!cAutoPlayGif()) {
|
if (!cAutoPlayGif()) {
|
||||||
Auth().data().stopAutoplayAnimations();
|
Auth().data().stopAutoplayAnimations();
|
||||||
}
|
}
|
||||||
const auto mode = (!autoplay && _data->isVideoMessage())
|
setClipReader(Media::Clip::MakeReader(
|
||||||
? Mode::Video
|
_data,
|
||||||
: Mode::Gif;
|
_parent->data()->fullId(),
|
||||||
setClipReader(Media::Clip::MakeReader(_data, _parent->data()->fullId(), [this](Media::Clip::Notification notification) {
|
[=](auto notification) { clipCallback(notification); },
|
||||||
_parent->clipCallback(notification);
|
Mode::Gif));
|
||||||
}, mode));
|
|
||||||
if (mode == Mode::Video) {
|
|
||||||
_roundPlayback = std::make_unique<Media::Clip::Playback>();
|
|
||||||
_roundPlayback->setValueChangedCallback([this](float64 value) {
|
|
||||||
Auth().data().requestViewRepaint(_parent);
|
|
||||||
});
|
|
||||||
if (App::main()) {
|
|
||||||
Auth().data().markMediaRead(_data);
|
|
||||||
}
|
|
||||||
App::wnd()->controller()->enableGifPauseReason(Window::GifPauseReason::RoundPlaying);
|
|
||||||
}
|
|
||||||
if (_gif && autoplay) {
|
if (_gif && autoplay) {
|
||||||
_gif->setAutoplay();
|
_gif->setAutoplay();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HistoryGif::isRoundVideoPlaying() const {
|
void HistoryGif::stopAnimation() {
|
||||||
return (_gif && _gif->mode() == Media::Clip::Reader::Mode::Video);
|
if (_gif) {
|
||||||
}
|
clearClipReader();
|
||||||
|
Auth().data().requestViewResize(_parent);
|
||||||
void HistoryGif::stopInline() {
|
_data->forget();
|
||||||
if (isRoundVideoPlaying()) {
|
|
||||||
App::wnd()->controller()->disableGifPauseReason(Window::GifPauseReason::RoundPlaying);
|
|
||||||
}
|
}
|
||||||
clearClipReader();
|
|
||||||
|
|
||||||
Auth().data().requestViewResize(_parent);
|
|
||||||
Auth().data().notifyViewLayoutChange(_parent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryGif::setClipReader(Media::Clip::ReaderPointer gif) {
|
void HistoryGif::setClipReader(Media::Clip::ReaderPointer gif) {
|
||||||
|
@ -3805,6 +3846,17 @@ void HistoryWebPage::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool p
|
||||||
_attach->clickHandlerPressedChanged(p, pressed);
|
_attach->clickHandlerPressedChanged(p, pressed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HistoryWebPage::playAnimation(bool autoplay) {
|
||||||
|
if (_attach) {
|
||||||
|
if (autoplay) {
|
||||||
|
_attach->autoplayAnimation();
|
||||||
|
} else {
|
||||||
|
_attach->playAnimation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool HistoryWebPage::isDisplayed() const {
|
bool HistoryWebPage::isDisplayed() const {
|
||||||
const auto item = _parent->data();
|
const auto item = _parent->data();
|
||||||
return !_data->pendingTill
|
return !_data->pendingTill
|
||||||
|
@ -4228,6 +4280,16 @@ ImagePtr HistoryGame::replyPreview() {
|
||||||
return _attach ? _attach->replyPreview() : (_data->photo ? _data->photo->makeReplyPreview() : ImagePtr());
|
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 {
|
QMargins HistoryGame::inBubblePadding() const {
|
||||||
auto lshift = st::msgPadding.left() + st::webPageLeft;
|
auto lshift = st::msgPadding.left() + st::webPageLeft;
|
||||||
auto rshift = st::msgPadding.right();
|
auto rshift = st::msgPadding.right();
|
||||||
|
|
|
@ -31,6 +31,10 @@ namespace Media {
|
||||||
namespace Clip {
|
namespace Clip {
|
||||||
class Playback;
|
class Playback;
|
||||||
} // namespace Clip
|
} // namespace Clip
|
||||||
|
|
||||||
|
namespace Player {
|
||||||
|
class RoundController;
|
||||||
|
} // namespace Player
|
||||||
} // namespace Media
|
} // namespace Media
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
@ -364,8 +368,6 @@ public:
|
||||||
return _data;
|
return _data;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool playInline(bool autoplay) override;
|
|
||||||
|
|
||||||
bool hasReplyPreview() const override {
|
bool hasReplyPreview() const override {
|
||||||
return !_data->thumb->isNull();
|
return !_data->thumb->isNull();
|
||||||
}
|
}
|
||||||
|
@ -447,13 +449,8 @@ public:
|
||||||
DocumentData *getDocument() const override {
|
DocumentData *getDocument() const override {
|
||||||
return _data;
|
return _data;
|
||||||
}
|
}
|
||||||
Media::Clip::Reader *getClipReader() override {
|
|
||||||
return _gif.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool playInline(bool autoplay) override;
|
void stopAnimation() override;
|
||||||
void stopInline() override;
|
|
||||||
bool isRoundVideoPlaying() const override;
|
|
||||||
|
|
||||||
bool hasReplyPreview() const override {
|
bool hasReplyPreview() const override {
|
||||||
return !_data->thumb->isNull();
|
return !_data->thumb->isNull();
|
||||||
|
@ -489,8 +486,14 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void playAnimation(bool autoplay) override;
|
||||||
QSize countOptimalSize() override;
|
QSize countOptimalSize() override;
|
||||||
QSize countCurrentSize(int newWidth) 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;
|
bool needInfoDisplay() const;
|
||||||
int additionalWidth(
|
int additionalWidth(
|
||||||
|
@ -506,8 +509,6 @@ private:
|
||||||
int _thumbw = 1;
|
int _thumbw = 1;
|
||||||
int _thumbh = 1;
|
int _thumbh = 1;
|
||||||
Text _caption;
|
Text _caption;
|
||||||
|
|
||||||
mutable std::unique_ptr<Media::Clip::Playback> _roundPlayback;
|
|
||||||
Media::Clip::ReaderPointer _gif;
|
Media::Clip::ReaderPointer _gif;
|
||||||
|
|
||||||
void setStatusSize(int newSize) const;
|
void setStatusSize(int newSize) const;
|
||||||
|
@ -732,14 +733,8 @@ public:
|
||||||
DocumentData *getDocument() const override {
|
DocumentData *getDocument() const override {
|
||||||
return _attach ? _attach->getDocument() : nullptr;
|
return _attach ? _attach->getDocument() : nullptr;
|
||||||
}
|
}
|
||||||
Media::Clip::Reader *getClipReader() override {
|
void stopAnimation() override {
|
||||||
return _attach ? _attach->getClipReader() : nullptr;
|
if (_attach) _attach->stopAnimation();
|
||||||
}
|
|
||||||
bool playInline(bool autoplay) override {
|
|
||||||
return _attach ? _attach->playInline(autoplay) : false;
|
|
||||||
}
|
|
||||||
void stopInline() override {
|
|
||||||
if (_attach) _attach->stopInline();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasReplyPreview() const override;
|
bool hasReplyPreview() const override;
|
||||||
|
@ -766,6 +761,7 @@ public:
|
||||||
~HistoryWebPage();
|
~HistoryWebPage();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void playAnimation(bool autoplay) override;
|
||||||
QSize countOptimalSize() override;
|
QSize countOptimalSize() override;
|
||||||
QSize countCurrentSize(int newWidth) override;
|
QSize countCurrentSize(int newWidth) override;
|
||||||
|
|
||||||
|
@ -842,14 +838,8 @@ public:
|
||||||
DocumentData *getDocument() const override {
|
DocumentData *getDocument() const override {
|
||||||
return _attach ? _attach->getDocument() : nullptr;
|
return _attach ? _attach->getDocument() : nullptr;
|
||||||
}
|
}
|
||||||
Media::Clip::Reader *getClipReader() override {
|
void stopAnimation() override {
|
||||||
return _attach ? _attach->getClipReader() : nullptr;
|
if (_attach) _attach->stopAnimation();
|
||||||
}
|
|
||||||
bool playInline(bool autoplay) override {
|
|
||||||
return _attach ? _attach->playInline(autoplay) : false;
|
|
||||||
}
|
|
||||||
void stopInline() override {
|
|
||||||
if (_attach) _attach->stopInline();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasReplyPreview() const override {
|
bool hasReplyPreview() const override {
|
||||||
|
@ -878,6 +868,7 @@ public:
|
||||||
~HistoryGame();
|
~HistoryGame();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void playAnimation(bool autoplay) override;
|
||||||
QSize countOptimalSize() override;
|
QSize countOptimalSize() override;
|
||||||
QSize countCurrentSize(int newWidth) override;
|
QSize countCurrentSize(int newWidth) override;
|
||||||
|
|
||||||
|
|
|
@ -573,11 +573,11 @@ HistoryWidget::HistoryWidget(QWidget *parent, not_null<Window::Controller*> cont
|
||||||
) | rpl::start_with_next([this](auto item) {
|
) | rpl::start_with_next([this](auto item) {
|
||||||
item->refreshMainView();
|
item->refreshMainView();
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
Auth().data().itemPlayInlineRequest(
|
Auth().data().animationPlayInlineRequest(
|
||||||
) | rpl::start_with_next([this](auto item) {
|
) | rpl::start_with_next([this](auto item) {
|
||||||
if (const auto view = item->mainView()) {
|
if (const auto view = item->mainView()) {
|
||||||
if (const auto media = view->media()) {
|
if (const auto media = view->media()) {
|
||||||
media->playInline(true);
|
media->playAnimation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
|
@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "history/view/history_view_element.h"
|
#include "history/view/history_view_element.h"
|
||||||
|
|
||||||
#include "media/media_clip_reader.h"
|
|
||||||
#include "history/history_item_components.h"
|
#include "history/history_item_components.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
#include "history/history_media.h"
|
#include "history/history_media.h"
|
||||||
|
@ -191,51 +190,6 @@ void Element::nextInBlocksRemoved() {
|
||||||
setAttachToNext(false);
|
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() {
|
void Element::refreshDataId() {
|
||||||
if (const auto media = this->media()) {
|
if (const auto media = this->media()) {
|
||||||
media->refreshParentId(data());
|
media->refreshParentId(data());
|
||||||
|
|
|
@ -37,6 +37,8 @@ public:
|
||||||
not_null<HistoryMessage*> message) = 0;
|
not_null<HistoryMessage*> message) = 0;
|
||||||
virtual std::unique_ptr<Element> elementCreate(
|
virtual std::unique_ptr<Element> elementCreate(
|
||||||
not_null<HistoryService*> message) = 0;
|
not_null<HistoryService*> message) = 0;
|
||||||
|
virtual void elementAnimationAutoplayAsync(
|
||||||
|
not_null<const Element*> element) = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -180,8 +182,6 @@ public:
|
||||||
void previousInBlocksChanged();
|
void previousInBlocksChanged();
|
||||||
void nextInBlocksRemoved();
|
void nextInBlocksRemoved();
|
||||||
|
|
||||||
void clipCallback(Media::Clip::Notification notification);
|
|
||||||
|
|
||||||
virtual ~Element();
|
virtual ~Element();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history_message.h"
|
#include "history/history_message.h"
|
||||||
#include "history/history_item_components.h"
|
#include "history/history_item_components.h"
|
||||||
#include "history/view/history_view_element.h"
|
#include "history/view/history_view_element.h"
|
||||||
|
#include "history/view/history_view_message.h"
|
||||||
#include "history/view/history_view_service_message.h"
|
#include "history/view/history_view_service_message.h"
|
||||||
#include "chat_helpers/message_field.h"
|
#include "chat_helpers/message_field.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
|
@ -223,13 +224,13 @@ ListWidget::ListWidget(
|
||||||
_scrollDateHideTimer.setCallback([this] { scrollDateHideByTimer(); });
|
_scrollDateHideTimer.setCallback([this] { scrollDateHideByTimer(); });
|
||||||
Auth().data().viewRepaintRequest(
|
Auth().data().viewRepaintRequest(
|
||||||
) | rpl::start_with_next([this](auto view) {
|
) | rpl::start_with_next([this](auto view) {
|
||||||
if (view->delegate() == _delegate) {
|
if (view->delegate() == this) {
|
||||||
repaintItem(view);
|
repaintItem(view);
|
||||||
}
|
}
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
Auth().data().viewResizeRequest(
|
Auth().data().viewResizeRequest(
|
||||||
) | rpl::start_with_next([this](auto view) {
|
) | rpl::start_with_next([this](auto view) {
|
||||||
if (view->delegate() == _delegate) {
|
if (view->delegate() == this) {
|
||||||
updateSize();
|
updateSize();
|
||||||
}
|
}
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
@ -239,11 +240,11 @@ ListWidget::ListWidget(
|
||||||
refreshItem(view);
|
refreshItem(view);
|
||||||
}
|
}
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
Auth().data().itemPlayInlineRequest(
|
Auth().data().animationPlayInlineRequest(
|
||||||
) | rpl::start_with_next([this](auto item) {
|
) | rpl::start_with_next([this](auto item) {
|
||||||
if (const auto view = viewForItem(item)) {
|
if (const auto view = viewForItem(item)) {
|
||||||
if (const auto media = view->media()) {
|
if (const auto media = view->media()) {
|
||||||
media->playInline(true);
|
media->playAnimation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
@ -329,7 +330,7 @@ not_null<Element*> ListWidget::enforceViewForItem(
|
||||||
}
|
}
|
||||||
const auto [i, ok] = _views.emplace(
|
const auto [i, ok] = _views.emplace(
|
||||||
item,
|
item,
|
||||||
item->createView(_delegate));
|
item->createView(this));
|
||||||
return i->second.get();
|
return i->second.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -511,6 +512,33 @@ QPoint ListWidget::tooltipPos() const {
|
||||||
return _mousePosition;
|
return _mousePosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Context ListWidget::elementContext() {
|
||||||
|
return _delegate->listContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Element> ListWidget::elementCreate(
|
||||||
|
not_null<HistoryMessage*> message) {
|
||||||
|
return std::make_unique<Message>(this, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Element> ListWidget::elementCreate(
|
||||||
|
not_null<HistoryService*> message) {
|
||||||
|
return std::make_unique<Service>(this, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListWidget::elementAnimationAutoplayAsync(
|
||||||
|
not_null<const Element*> 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<ListMemento*> memento) {
|
void ListWidget::saveState(not_null<ListMemento*> memento) {
|
||||||
memento->setAroundPosition(_aroundPosition);
|
memento->setAroundPosition(_aroundPosition);
|
||||||
auto state = countScrollState();
|
auto state = countScrollState();
|
||||||
|
@ -1432,7 +1460,7 @@ void ListWidget::refreshItem(not_null<const Element*> view) {
|
||||||
_views.erase(item);
|
_views.erase(item);
|
||||||
const auto [i, ok] = _views.emplace(
|
const auto [i, ok] = _views.emplace(
|
||||||
item,
|
item,
|
||||||
item->createView(_delegate));
|
item->createView(this));
|
||||||
const auto was = view;
|
const auto was = view;
|
||||||
const auto now = i->second.get();
|
const auto now = i->second.get();
|
||||||
_items[index] = now;
|
_items[index] = now;
|
||||||
|
|
|
@ -27,7 +27,7 @@ namespace HistoryView {
|
||||||
|
|
||||||
enum class Context : char;
|
enum class Context : char;
|
||||||
|
|
||||||
class ListDelegate : public ElementDelegate {
|
class ListDelegate {
|
||||||
public:
|
public:
|
||||||
virtual Context listContext() = 0;
|
virtual Context listContext() = 0;
|
||||||
virtual void listScrollTo(int top) = 0;
|
virtual void listScrollTo(int top) = 0;
|
||||||
|
@ -77,6 +77,7 @@ private:
|
||||||
|
|
||||||
class ListWidget final
|
class ListWidget final
|
||||||
: public Ui::RpWidget
|
: public Ui::RpWidget
|
||||||
|
, public ElementDelegate
|
||||||
, public Ui::AbstractTooltipShower
|
, public Ui::AbstractTooltipShower
|
||||||
, private base::Subscriber {
|
, private base::Subscriber {
|
||||||
public:
|
public:
|
||||||
|
@ -100,6 +101,15 @@ public:
|
||||||
QString tooltipText() const override;
|
QString tooltipText() const override;
|
||||||
QPoint tooltipPos() const override;
|
QPoint tooltipPos() const override;
|
||||||
|
|
||||||
|
// ElementDelegate interface.
|
||||||
|
Context elementContext() override;
|
||||||
|
std::unique_ptr<Element> elementCreate(
|
||||||
|
not_null<HistoryMessage*> message) override;
|
||||||
|
std::unique_ptr<Element> elementCreate(
|
||||||
|
not_null<HistoryService*> message) override;
|
||||||
|
void elementAnimationAutoplayAsync(
|
||||||
|
not_null<const Element*> view) override;
|
||||||
|
|
||||||
~ListWidget();
|
~ListWidget();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -171,15 +171,25 @@ StackItemSection::StackItemSection(
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ToggleCallback, typename DraggedCallback>
|
template <typename ToggleCallback, typename DraggedCallback>
|
||||||
MainWidget::Float::Float(QWidget *parent, HistoryItem *item, ToggleCallback toggle, DraggedCallback dragged)
|
MainWidget::Float::Float(
|
||||||
|
QWidget *parent,
|
||||||
|
not_null<Window::Controller*> controller,
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
|
ToggleCallback toggle,
|
||||||
|
DraggedCallback dragged)
|
||||||
: animationSide(RectPart::Right)
|
: animationSide(RectPart::Right)
|
||||||
, column(Window::Column::Second)
|
, column(Window::Column::Second)
|
||||||
, corner(RectPart::TopRight)
|
, corner(RectPart::TopRight)
|
||||||
, widget(parent, item, [this, toggle = std::move(toggle)](bool visible) {
|
, widget(
|
||||||
toggle(this, visible);
|
parent,
|
||||||
}, [this, dragged = std::move(dragged)](bool closed) {
|
controller,
|
||||||
dragged(this, closed);
|
item,
|
||||||
}) {
|
[this, toggle = std::move(toggle)](bool visible) {
|
||||||
|
toggle(this, visible);
|
||||||
|
},
|
||||||
|
[this, dragged = std::move(dragged)](bool closed) {
|
||||||
|
dragged(this, closed);
|
||||||
|
}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWidget::MainWidget(
|
MainWidget::MainWidget(
|
||||||
|
@ -317,33 +327,45 @@ MainWidget::MainWidget(
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::checkCurrentFloatPlayer() {
|
void MainWidget::checkCurrentFloatPlayer() {
|
||||||
auto state = Media::Player::instance()->current(AudioMsgId::Type::Voice);
|
const auto state = Media::Player::instance()->current(AudioMsgId::Type::Voice);
|
||||||
auto fullId = state.contextId();
|
const auto fullId = state.contextId();
|
||||||
auto last = currentFloatPlayer();
|
const auto last = currentFloatPlayer();
|
||||||
if (!last || last->widget->detached() || last->widget->item()->fullId() != fullId) {
|
if (last
|
||||||
if (last) {
|
&& !last->widget->detached()
|
||||||
last->widget->detach();
|
&& last->widget->item()->fullId() == fullId) {
|
||||||
}
|
return;
|
||||||
if (auto item = App::histItemById(fullId)) {
|
}
|
||||||
if (auto media = item->media()) {
|
if (last) {
|
||||||
if (auto document = media->document()) {
|
last->widget->detach();
|
||||||
if (document->isVideoMessage()) {
|
}
|
||||||
_playerFloats.push_back(std::make_unique<Float>(this, item, [this](not_null<Float*> instance, bool visible) {
|
if (const auto item = App::histItemById(fullId)) {
|
||||||
instance->hiddenByWidget = !visible;
|
if (const auto media = item->media()) {
|
||||||
toggleFloatPlayer(instance);
|
if (const auto document = media->document()) {
|
||||||
}, [this](not_null<Float*> instance, bool closed) {
|
if (document->isVideoMessage()) {
|
||||||
finishFloatPlayerDrag(instance, closed);
|
createFloatPlayer(item);
|
||||||
}));
|
|
||||||
currentFloatPlayer()->column = Auth().settings().floatPlayerColumn();
|
|
||||||
currentFloatPlayer()->corner = Auth().settings().floatPlayerCorner();
|
|
||||||
checkFloatPlayerVisibility();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWidget::createFloatPlayer(not_null<HistoryItem*> item) {
|
||||||
|
_playerFloats.push_back(std::make_unique<Float>(
|
||||||
|
this,
|
||||||
|
_controller,
|
||||||
|
item,
|
||||||
|
[this](not_null<Float*> instance, bool visible) {
|
||||||
|
instance->hiddenByWidget = !visible;
|
||||||
|
toggleFloatPlayer(instance);
|
||||||
|
},
|
||||||
|
[this](not_null<Float*> instance, bool closed) {
|
||||||
|
finishFloatPlayerDrag(instance, closed);
|
||||||
|
}));
|
||||||
|
currentFloatPlayer()->column = Auth().settings().floatPlayerColumn();
|
||||||
|
currentFloatPlayer()->corner = Auth().settings().floatPlayerCorner();
|
||||||
|
checkFloatPlayerVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
void MainWidget::toggleFloatPlayer(not_null<Float*> instance) {
|
void MainWidget::toggleFloatPlayer(not_null<Float*> instance) {
|
||||||
auto visible = !instance->hiddenByHistory && !instance->hiddenByWidget && instance->widget->isReady();
|
auto visible = !instance->hiddenByHistory && !instance->hiddenByWidget && instance->widget->isReady();
|
||||||
if (instance->visible != visible) {
|
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)) {
|
if (!Media::Player::IsStoppedOrStopping(state.state)) {
|
||||||
createPlayer();
|
createPlayer();
|
||||||
}
|
}
|
||||||
|
@ -1587,7 +1609,6 @@ void MainWidget::handleAudioUpdate(const AudioMsgId &audioId) {
|
||||||
|
|
||||||
if (const auto item = App::histItemById(audioId.contextId())) {
|
if (const auto item = App::histItemById(audioId.contextId())) {
|
||||||
Auth().data().requestItemRepaint(item);
|
Auth().data().requestItemRepaint(item);
|
||||||
item->audioTrackUpdated();
|
|
||||||
}
|
}
|
||||||
if (const auto items = InlineBots::Layout::documentItems()) {
|
if (const auto items = InlineBots::Layout::documentItems()) {
|
||||||
if (const auto i = items->find(document); i != items->end()) {
|
if (const auto i = items->find(document); i != items->end()) {
|
||||||
|
|
|
@ -380,7 +380,12 @@ protected:
|
||||||
private:
|
private:
|
||||||
struct Float {
|
struct Float {
|
||||||
template <typename ToggleCallback, typename DraggedCallback>
|
template <typename ToggleCallback, typename DraggedCallback>
|
||||||
Float(QWidget *parent, HistoryItem *item, ToggleCallback callback, DraggedCallback dragged);
|
Float(
|
||||||
|
QWidget *parent,
|
||||||
|
not_null<Window::Controller*> controller,
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
|
ToggleCallback toggle,
|
||||||
|
DraggedCallback dragged);
|
||||||
|
|
||||||
bool hiddenByWidget = false;
|
bool hiddenByWidget = false;
|
||||||
bool hiddenByHistory = false;
|
bool hiddenByHistory = false;
|
||||||
|
@ -506,6 +511,7 @@ private:
|
||||||
|
|
||||||
void clearCachedBackground();
|
void clearCachedBackground();
|
||||||
void checkCurrentFloatPlayer();
|
void checkCurrentFloatPlayer();
|
||||||
|
void createFloatPlayer(not_null<HistoryItem*> item);
|
||||||
void toggleFloatPlayer(not_null<Float*> instance);
|
void toggleFloatPlayer(not_null<Float*> instance);
|
||||||
void checkFloatPlayerVisibility();
|
void checkFloatPlayerVisibility();
|
||||||
void updateFloatPlayerPosition(not_null<Float*> instance);
|
void updateFloatPlayerPosition(not_null<Float*> instance);
|
||||||
|
|
|
@ -15,8 +15,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
#include "history/view/history_view_element.h"
|
#include "history/view/history_view_element.h"
|
||||||
#include "media/media_clip_reader.h"
|
#include "media/media_clip_reader.h"
|
||||||
#include "media/view/media_clip_playback.h"
|
|
||||||
#include "media/media_audio.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 "auth_session.h"
|
||||||
#include "styles/style_media_player.h"
|
#include "styles/style_media_player.h"
|
||||||
#include "styles/style_history.h"
|
#include "styles/style_history.h"
|
||||||
|
@ -26,10 +28,12 @@ namespace Player {
|
||||||
|
|
||||||
Float::Float(
|
Float::Float(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
HistoryItem *item,
|
not_null<Window::Controller*> controller,
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
base::lambda<void(bool visible)> toggleCallback,
|
base::lambda<void(bool visible)> toggleCallback,
|
||||||
base::lambda<void(bool closed)> draggedCallback)
|
base::lambda<void(bool closed)> draggedCallback)
|
||||||
: RpWidget(parent)
|
: RpWidget(parent)
|
||||||
|
, _controller(controller)
|
||||||
, _item(item)
|
, _item(item)
|
||||||
, _toggleCallback(std::move(toggleCallback))
|
, _toggleCallback(std::move(toggleCallback))
|
||||||
, _draggedCallback(std::move(draggedCallback)) {
|
, _draggedCallback(std::move(draggedCallback)) {
|
||||||
|
@ -47,7 +51,7 @@ Float::Float(
|
||||||
prepareShadow();
|
prepareShadow();
|
||||||
|
|
||||||
rpl::merge(
|
rpl::merge(
|
||||||
//Auth().data().viewLayoutChanged(
|
//Auth().data().viewLayoutChanged( // #TODO not needed?
|
||||||
//) | rpl::map(
|
//) | rpl::map(
|
||||||
// [](auto view) { return view->data(); }
|
// [](auto view) { return view->data(); }
|
||||||
//),
|
//),
|
||||||
|
@ -103,12 +107,10 @@ float64 Float::outRatio() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Float::mouseReleaseEvent(QMouseEvent *e) {
|
void Float::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
if (_down) {
|
if (base::take(_down) && _item) {
|
||||||
_down = false;
|
if (const auto controller = _controller->roundVideo(_item)) {
|
||||||
// #TODO float player
|
controller->pauseResume();
|
||||||
//if (auto media = _item ? _item->getMedia() : nullptr) {
|
}
|
||||||
// media->playInline();
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
if (_drag) {
|
if (_drag) {
|
||||||
finishDrag(outRatio() < 0.5);
|
finishDrag(outRatio() < 0.5);
|
||||||
|
@ -125,10 +127,9 @@ void Float::finishDrag(bool closed) {
|
||||||
void Float::mouseDoubleClickEvent(QMouseEvent *e) {
|
void Float::mouseDoubleClickEvent(QMouseEvent *e) {
|
||||||
if (_item) {
|
if (_item) {
|
||||||
// Handle second click.
|
// Handle second click.
|
||||||
// #TODO float player
|
if (const auto controller = _controller->roundVideo(_item)) {
|
||||||
//if (auto media = _item->getMedia()) {
|
controller->pauseResume();
|
||||||
// media->playInline();
|
}
|
||||||
//}
|
|
||||||
Ui::showPeerHistoryAtItem(_item);
|
Ui::showPeerHistoryAtItem(_item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -175,7 +176,8 @@ void Float::paintEvent(QPaintEvent *e) {
|
||||||
auto inner = getInnerRect();
|
auto inner = getInnerRect();
|
||||||
p.drawImage(inner.topLeft(), _frame);
|
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.) {
|
if (progress > 0.) {
|
||||||
auto pen = st::historyVideoMessageProgressFg->p;
|
auto pen = st::historyVideoMessageProgressFg->p;
|
||||||
auto was = p.pen();
|
auto was = p.pen();
|
||||||
|
@ -198,19 +200,31 @@ void Float::paintEvent(QPaintEvent *e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Clip::Reader *Float::getReader() const {
|
Clip::Reader *Float::getReader() const {
|
||||||
// #TODO float player
|
if (detached()) {
|
||||||
//if (auto media = _item ? _item->getMedia() : nullptr) {
|
return nullptr;
|
||||||
// if (auto reader = media->getClipReader()) {
|
}
|
||||||
// if (reader->started() && reader->mode() == Clip::Reader::Mode::Video) {
|
if (const auto controller = _controller->roundVideo(_item)) {
|
||||||
// return reader;
|
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;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Float::hasFrame() const {
|
bool Float::hasFrame() const {
|
||||||
if (auto reader = getReader()) {
|
if (const auto reader = getReader()) {
|
||||||
return !reader->current().isNull();
|
return !reader->current().isNull();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -219,14 +233,15 @@ bool Float::hasFrame() const {
|
||||||
bool Float::fillFrame() {
|
bool Float::fillFrame() {
|
||||||
auto creating = _frame.isNull();
|
auto creating = _frame.isNull();
|
||||||
if (creating) {
|
if (creating) {
|
||||||
_frame = QImage(getInnerRect().size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
|
_frame = QImage(
|
||||||
|
getInnerRect().size() * cIntRetinaFactor(),
|
||||||
|
QImage::Format_ARGB32_Premultiplied);
|
||||||
_frame.setDevicePixelRatio(cRetinaFactor());
|
_frame.setDevicePixelRatio(cRetinaFactor());
|
||||||
}
|
}
|
||||||
auto frameInner = [this] {
|
auto frameInner = [&] {
|
||||||
return QRect(0, 0, _frame.width() / cIntRetinaFactor(), _frame.height() / cIntRetinaFactor());
|
return QRect(QPoint(), _frame.size() / cIntRetinaFactor());
|
||||||
};
|
};
|
||||||
if (auto reader = getReader()) {
|
if (const auto reader = getReader()) {
|
||||||
updatePlayback();
|
|
||||||
auto frame = reader->current();
|
auto frame = reader->current();
|
||||||
if (!frame.isNull()) {
|
if (!frame.isNull()) {
|
||||||
_frame.fill(Qt::transparent);
|
_frame.fill(Qt::transparent);
|
||||||
|
@ -249,21 +264,6 @@ bool Float::fillFrame() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Float::updatePlayback() {
|
|
||||||
if (_item) {
|
|
||||||
if (!_roundPlayback) {
|
|
||||||
_roundPlayback = std::make_unique<Media::Clip::Playback>();
|
|
||||||
_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() {
|
void Float::repaintItem() {
|
||||||
update();
|
update();
|
||||||
if (hasFrame() && _toggleCallback) {
|
if (hasFrame() && _toggleCallback) {
|
||||||
|
|
|
@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "ui/rp_widget.h"
|
#include "ui/rp_widget.h"
|
||||||
|
|
||||||
|
namespace Window {
|
||||||
|
class Controller;
|
||||||
|
} // namespace Window
|
||||||
|
|
||||||
namespace Media {
|
namespace Media {
|
||||||
namespace Clip {
|
namespace Clip {
|
||||||
class Playback;
|
class Playback;
|
||||||
|
@ -18,7 +22,12 @@ namespace Player {
|
||||||
|
|
||||||
class Float : public Ui::RpWidget, private base::Subscriber {
|
class Float : public Ui::RpWidget, private base::Subscriber {
|
||||||
public:
|
public:
|
||||||
Float(QWidget *parent, HistoryItem *item, base::lambda<void(bool visible)> toggleCallback, base::lambda<void(bool closed)> draggedCallback);
|
Float(
|
||||||
|
QWidget *parent,
|
||||||
|
not_null<Window::Controller*> controller,
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
|
base::lambda<void(bool visible)> toggleCallback,
|
||||||
|
base::lambda<void(bool closed)> draggedCallback);
|
||||||
|
|
||||||
HistoryItem *item() const {
|
HistoryItem *item() const {
|
||||||
return _item;
|
return _item;
|
||||||
|
@ -59,14 +68,15 @@ protected:
|
||||||
private:
|
private:
|
||||||
float64 outRatio() const;
|
float64 outRatio() const;
|
||||||
Clip::Reader *getReader() const;
|
Clip::Reader *getReader() const;
|
||||||
|
Clip::Playback *getPlayback() const;
|
||||||
void repaintItem();
|
void repaintItem();
|
||||||
void prepareShadow();
|
void prepareShadow();
|
||||||
bool hasFrame() const;
|
bool hasFrame() const;
|
||||||
bool fillFrame();
|
bool fillFrame();
|
||||||
QRect getInnerRect() const;
|
QRect getInnerRect() const;
|
||||||
void updatePlayback();
|
|
||||||
void finishDrag(bool closed);
|
void finishDrag(bool closed);
|
||||||
|
|
||||||
|
not_null<Window::Controller*> _controller;
|
||||||
HistoryItem *_item = nullptr;
|
HistoryItem *_item = nullptr;
|
||||||
base::lambda<void(bool visible)> _toggleCallback;
|
base::lambda<void(bool visible)> _toggleCallback;
|
||||||
|
|
||||||
|
@ -81,8 +91,6 @@ private:
|
||||||
QPoint _dragLocalPoint;
|
QPoint _dragLocalPoint;
|
||||||
base::lambda<void(bool closed)> _draggedCallback;
|
base::lambda<void(bool closed)> _draggedCallback;
|
||||||
|
|
||||||
std::unique_ptr<Clip::Playback> _roundPlayback;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Player
|
} // namespace Player
|
||||||
|
|
|
@ -10,12 +10,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
#include "media/media_audio.h"
|
#include "media/media_audio.h"
|
||||||
#include "media/media_audio_capture.h"
|
#include "media/media_audio_capture.h"
|
||||||
#include "messenger.h"
|
|
||||||
#include "auth_session.h"
|
|
||||||
#include "calls/calls_instance.h"
|
#include "calls/calls_instance.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
#include "data/data_media_types.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 Media {
|
||||||
namespace Player {
|
namespace Player {
|
||||||
|
@ -230,13 +232,15 @@ bool Instance::moveInPlaylist(
|
||||||
item->fullId()
|
item->fullId()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (document->isAudioFile()) {
|
if (document->isAudioFile()
|
||||||
|
|| document->isVoiceMessage()
|
||||||
|
|| document->isVideoMessage()) {
|
||||||
play(AudioMsgId(document, item->fullId()));
|
play(AudioMsgId(document, item->fullId()));
|
||||||
} else {
|
} else {
|
||||||
DocumentOpenClickHandler::doOpen(
|
//DocumentOpenClickHandler::doOpen(
|
||||||
document,
|
// document,
|
||||||
item,
|
// item,
|
||||||
ActionOnLoadPlayInline);
|
// ActionOnLoadPlayInline);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -300,12 +304,9 @@ void Instance::play(const AudioMsgId &audioId) {
|
||||||
documentLoadProgress(document);
|
documentLoadProgress(document);
|
||||||
}
|
}
|
||||||
} else if (document->isVideoMessage()) {
|
} else if (document->isVideoMessage()) {
|
||||||
// #TODO float player
|
if (const auto item = App::histItemById(audioId.contextId())) {
|
||||||
//if (const auto item = App::histItemById(audioId.contextId())) {
|
App::wnd()->controller()->startRoundVideo(item);
|
||||||
// if (const auto media = item->getMedia()) {
|
}
|
||||||
// media->playInline();
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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> RoundController::TryStart(
|
||||||
|
not_null<Window::Controller*> parent,
|
||||||
|
not_null<HistoryItem*> 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<RoundController>(CreateTag(), parent, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
RoundController::RoundController(
|
||||||
|
CreateTag&&,
|
||||||
|
not_null<Window::Controller*> parent,
|
||||||
|
not_null<HistoryItem*> 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<Clip::Playback>();
|
||||||
|
_playback->setValueChangedCallback([=](float64 value) {
|
||||||
|
Auth().data().requestItemRepaint(_context);
|
||||||
|
});
|
||||||
|
Auth().data().markMediaRead(_data);
|
||||||
|
Auth().data().itemRemoved(
|
||||||
|
) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
|
||||||
|
if (item == _context) {
|
||||||
|
stop(State::Stopped);
|
||||||
|
}
|
||||||
|
}, lifetime());
|
||||||
|
Auth().data().itemRepaintRequest(
|
||||||
|
) | rpl::start_with_next([=](not_null<const HistoryItem*> 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
|
|
@ -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<RoundController> TryStart(
|
||||||
|
not_null<Window::Controller*> parent,
|
||||||
|
not_null<HistoryItem*> item);
|
||||||
|
|
||||||
|
FullMsgId contextId() const;
|
||||||
|
void pauseResume();
|
||||||
|
Clip::Reader *reader() const;
|
||||||
|
Clip::Playback *playback() const;
|
||||||
|
|
||||||
|
rpl::lifetime &lifetime();
|
||||||
|
|
||||||
|
RoundController(
|
||||||
|
CreateTag&&,
|
||||||
|
not_null<Window::Controller*> parent,
|
||||||
|
not_null<HistoryItem*> item);
|
||||||
|
~RoundController();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void stop(State state);
|
||||||
|
bool checkReaderState();
|
||||||
|
void callback(Clip::Notification notification);
|
||||||
|
void handleAudioUpdate(const TrackState &audioId);
|
||||||
|
|
||||||
|
not_null<Window::Controller*> _parent;
|
||||||
|
not_null<DocumentData*> _data;
|
||||||
|
not_null<HistoryItem*> _context;
|
||||||
|
Clip::ReaderPointer _reader;
|
||||||
|
std::unique_ptr<Clip::Playback> _playback;
|
||||||
|
|
||||||
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Player
|
||||||
|
} // namespace Media
|
|
@ -12,16 +12,30 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
#include "history/view/history_view_element.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 "mainwidget.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "styles/style_window.h"
|
|
||||||
#include "styles/style_dialogs.h"
|
|
||||||
#include "boxes/calendar_box.h"
|
|
||||||
#include "auth_session.h"
|
#include "auth_session.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
|
#include "styles/style_window.h"
|
||||||
|
#include "styles/style_dialogs.h"
|
||||||
|
|
||||||
namespace Window {
|
namespace Window {
|
||||||
|
|
||||||
|
Controller::Controller(not_null<MainWindow*> 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) {
|
void Controller::enableGifPauseReason(GifPauseReason reason) {
|
||||||
if (!(_gifPauseReasons & reason)) {
|
if (!(_gifPauseReasons & reason)) {
|
||||||
auto notify = (static_cast<int>(_gifPauseReasons) < static_cast<int>(reason));
|
auto notify = (static_cast<int>(_gifPauseReasons) < static_cast<int>(reason));
|
||||||
|
@ -405,4 +419,40 @@ not_null<MainWidget*> Controller::chats() const {
|
||||||
return App::wnd()->chatsWidget();
|
return App::wnd()->chatsWidget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Controller::startRoundVideo(not_null<HistoryItem*> 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<const HistoryItem*> 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<RoundController*> video) {
|
||||||
|
if (video == _roundVideo.get()) {
|
||||||
|
_roundVideo = nullptr;
|
||||||
|
disableGifPauseReason(Window::GifPauseReason::RoundPlaying);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Controller::~Controller() = default;
|
||||||
|
|
||||||
} // namespace Window
|
} // namespace Window
|
||||||
|
|
|
@ -14,6 +14,12 @@ class MainWidget;
|
||||||
class HistoryMessage;
|
class HistoryMessage;
|
||||||
class HistoryService;
|
class HistoryService;
|
||||||
|
|
||||||
|
namespace Media {
|
||||||
|
namespace Player {
|
||||||
|
class RoundController;
|
||||||
|
} // namespace Player
|
||||||
|
} // namespace Media
|
||||||
|
|
||||||
namespace Window {
|
namespace Window {
|
||||||
|
|
||||||
class LayerWidget;
|
class LayerWidget;
|
||||||
|
@ -89,12 +95,13 @@ public:
|
||||||
not_null<History*> history,
|
not_null<History*> history,
|
||||||
const SectionShow ¶ms = SectionShow());
|
const SectionShow ¶ms = SectionShow());
|
||||||
|
|
||||||
|
virtual ~Navigation() = default;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Controller : public Navigation {
|
class Controller : public Navigation {
|
||||||
public:
|
public:
|
||||||
Controller(not_null<MainWindow*> window) : _window(window) {
|
Controller(not_null<MainWindow*> window);
|
||||||
}
|
|
||||||
|
|
||||||
not_null<MainWindow*> window() const {
|
not_null<MainWindow*> window() const {
|
||||||
return _window;
|
return _window;
|
||||||
|
@ -197,6 +204,19 @@ public:
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using RoundController = Media::Player::RoundController;
|
||||||
|
bool startRoundVideo(not_null<HistoryItem*> context);
|
||||||
|
RoundController *currentRoundVideo() const;
|
||||||
|
RoundController *roundVideo(not_null<const HistoryItem*> context) const;
|
||||||
|
RoundController *roundVideo(FullMsgId contextId) const;
|
||||||
|
void roundVideoFinished(not_null<RoundController*> video);
|
||||||
|
|
||||||
|
rpl::lifetime &lifetime() {
|
||||||
|
return _lifetime;
|
||||||
|
}
|
||||||
|
|
||||||
|
~Controller();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int minimalThreeColumnWidth() const;
|
int minimalThreeColumnWidth() const;
|
||||||
not_null<MainWidget*> chats() const;
|
not_null<MainWidget*> chats() const;
|
||||||
|
@ -220,6 +240,10 @@ private:
|
||||||
base::Variable<bool> _dialogsListFocused = { false };
|
base::Variable<bool> _dialogsListFocused = { false };
|
||||||
base::Variable<bool> _dialogsListDisplayForced = { false };
|
base::Variable<bool> _dialogsListDisplayForced = { false };
|
||||||
|
|
||||||
|
std::unique_ptr<RoundController> _roundVideo;
|
||||||
|
|
||||||
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Window
|
} // namespace Window
|
||||||
|
|
|
@ -360,6 +360,8 @@
|
||||||
<(src_loc)/media/player/media_player_instance.h
|
<(src_loc)/media/player/media_player_instance.h
|
||||||
<(src_loc)/media/player/media_player_panel.cpp
|
<(src_loc)/media/player/media_player_panel.cpp
|
||||||
<(src_loc)/media/player/media_player_panel.h
|
<(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.cpp
|
||||||
<(src_loc)/media/player/media_player_volume_controller.h
|
<(src_loc)/media/player/media_player_volume_controller.h
|
||||||
<(src_loc)/media/player/media_player_widget.cpp
|
<(src_loc)/media/player/media_player_widget.cpp
|
||||||
|
|
Loading…
Add table
Reference in a new issue