Animate video userpics in chat history.

This commit is contained in:
John Preston 2022-05-16 16:32:24 +04:00
parent 201edb2e69
commit 73bacfc650
9 changed files with 129 additions and 45 deletions

View file

@ -401,6 +401,8 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
return; return;
} }
const auto activeEntry = _controller->activeChatEntryCurrent(); const auto activeEntry = _controller->activeChatEntryCurrent();
const auto videoPaused = _controller->isGifPausedAtLeastFor(
Window::GifPauseReason::Any);
auto fullWidth = width(); auto fullWidth = width();
auto dialogsClip = r; auto dialogsClip = r;
auto ms = crl::now(); auto ms = crl::now();
@ -449,7 +451,8 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
fullWidth, fullWidth,
isActive, isActive,
isSelected, isSelected,
ms); ms,
videoPaused);
if (xadd || yadd) { if (xadd || yadd) {
p.translate(-xadd, -yadd); p.translate(-xadd, -yadd);
} }
@ -568,7 +571,8 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
fullWidth, fullWidth,
active, active,
selected, selected,
ms); ms,
videoPaused);
p.translate(0, st::dialogsRowHeight); p.translate(0, st::dialogsRowHeight);
} }
} }
@ -686,13 +690,13 @@ Ui::VideoUserpic *InnerWidget::validateVideoUserpic(
if (i != end(_videoUserpics)) { if (i != end(_videoUserpics)) {
return i->second.get(); return i->second.get();
} }
const auto update = [=] { const auto repaint = [=] {
updateDialogRow({ history, FullMsgId() }); updateDialogRow({ history, FullMsgId() });
updateSearchResult(history->peer); updateSearchResult(history->peer);
}; };
return _videoUserpics.emplace(peer, std::make_unique<Ui::VideoUserpic>( return _videoUserpics.emplace(peer, std::make_unique<Ui::VideoUserpic>(
peer, peer,
update repaint
)).first->second.get(); )).first->second.get();
} }

View file

@ -80,22 +80,6 @@ namespace {
: accumulated; : accumulated;
} }
void PaintUserpic(
Painter &p,
not_null<PeerData*> peer,
Ui::VideoUserpic *videoUserpic,
std::shared_ptr<Data::CloudImageView> &view,
int x,
int y,
int outerWidth,
int size) {
if (videoUserpic) {
videoUserpic->paintLeft(p, view, x, y, outerWidth, size);
} else {
peer->paintUserpicLeft(p, view, x, y, outerWidth, size);
}
}
} // namespace } // namespace
BasicRow::BasicRow() = default; BasicRow::BasicRow() = default;
@ -142,7 +126,8 @@ void BasicRow::paintUserpic(
History *historyForCornerBadge, History *historyForCornerBadge,
crl::time now, crl::time now,
bool active, bool active,
int fullWidth) const { int fullWidth,
bool paused) const {
PaintUserpic( PaintUserpic(
p, p,
peer, peer,
@ -151,7 +136,8 @@ void BasicRow::paintUserpic(
st::dialogsPadding.x(), st::dialogsPadding.x(),
st::dialogsPadding.y(), st::dialogsPadding.y(),
fullWidth, fullWidth,
st::dialogsPhotoSize); st::dialogsPhotoSize,
paused);
} }
Row::Row(Key key, int pos) : _id(key), _pos(pos) { Row::Row(Key key, int pos) : _id(key), _pos(pos) {
@ -232,7 +218,8 @@ void Row::PaintCornerBadgeFrame(
not_null<CornerBadgeUserpic*> data, not_null<CornerBadgeUserpic*> data,
not_null<PeerData*> peer, not_null<PeerData*> peer,
Ui::VideoUserpic *videoUserpic, Ui::VideoUserpic *videoUserpic,
std::shared_ptr<Data::CloudImageView> &view) { std::shared_ptr<Data::CloudImageView> &view,
bool paused) {
data->frame.fill(Qt::transparent); data->frame.fill(Qt::transparent);
Painter q(&data->frame); Painter q(&data->frame);
@ -244,7 +231,8 @@ void Row::PaintCornerBadgeFrame(
0, 0,
0, 0,
data->frame.width() / data->frame.devicePixelRatio(), data->frame.width() / data->frame.devicePixelRatio(),
st::dialogsPhotoSize); st::dialogsPhotoSize,
paused);
PainterHighQualityEnabler hq(q); PainterHighQualityEnabler hq(q);
q.setCompositionMode(QPainter::CompositionMode_Source); q.setCompositionMode(QPainter::CompositionMode_Source);
@ -279,7 +267,8 @@ void Row::paintUserpic(
History *historyForCornerBadge, History *historyForCornerBadge,
crl::time now, crl::time now,
bool active, bool active,
int fullWidth) const { int fullWidth,
bool paused) const {
updateCornerBadgeShown(peer); updateCornerBadgeShown(peer);
const auto shown = _cornerBadgeUserpic const auto shown = _cornerBadgeUserpic
@ -293,7 +282,8 @@ void Row::paintUserpic(
historyForCornerBadge, historyForCornerBadge,
now, now,
active, active,
fullWidth); fullWidth,
paused);
if (!historyForCornerBadge || !_cornerBadgeShown) { if (!historyForCornerBadge || !_cornerBadgeShown) {
_cornerBadgeUserpic = nullptr; _cornerBadgeUserpic = nullptr;
} }
@ -322,7 +312,8 @@ void Row::paintUserpic(
_cornerBadgeUserpic.get(), _cornerBadgeUserpic.get(),
peer, peer,
videoUserpic, videoUserpic,
userpicView()); userpicView(),
paused);
} }
p.drawImage(st::dialogsPadding, _cornerBadgeUserpic->frame); p.drawImage(st::dialogsPadding, _cornerBadgeUserpic->frame);
if (historyForCornerBadge->peer->isUser()) { if (historyForCornerBadge->peer->isUser()) {

View file

@ -45,7 +45,8 @@ public:
History *historyForCornerBadge, History *historyForCornerBadge,
crl::time now, crl::time now,
bool active, bool active,
int fullWidth) const; int fullWidth,
bool paused) const;
void addRipple(QPoint origin, QSize size, Fn<void()> updateCallback); void addRipple(QPoint origin, QSize size, Fn<void()> updateCallback);
void stopLastRipple(); void stopLastRipple();
@ -84,7 +85,8 @@ public:
History *historyForCornerBadge, History *historyForCornerBadge,
crl::time now, crl::time now,
bool active, bool active,
int fullWidth) const final override; int fullWidth,
bool paused) const final override;
[[nodiscard]] Key key() const { [[nodiscard]] Key key() const {
return _id; return _id;
@ -131,7 +133,8 @@ private:
not_null<CornerBadgeUserpic*> data, not_null<CornerBadgeUserpic*> data,
not_null<PeerData*> peer, not_null<PeerData*> peer,
Ui::VideoUserpic *videoUserpic, Ui::VideoUserpic *videoUserpic,
std::shared_ptr<Data::CloudImageView> &view); std::shared_ptr<Data::CloudImageView> &view,
bool paused);
Key _id; Key _id;
int _pos = 0; int _pos = 0;

View file

@ -302,6 +302,7 @@ enum class Flag {
SavedMessages = 0x08, SavedMessages = 0x08,
RepliesMessages = 0x10, RepliesMessages = 0x10,
AllowUserOnline = 0x20, AllowUserOnline = 0x20,
VideoPaused = 0x40,
}; };
inline constexpr bool is_flag_type(Flag) { return true; } inline constexpr bool is_flag_type(Flag) { return true; }
@ -366,7 +367,8 @@ void paintRow(
(flags & Flag::AllowUserOnline) ? history : nullptr, (flags & Flag::AllowUserOnline) ? history : nullptr,
ms, ms,
active, active,
fullWidth); fullWidth,
(flags & Flag::VideoPaused));
} else if (hiddenSenderInfo) { } else if (hiddenSenderInfo) {
hiddenSenderInfo->emptyUserpic.paint( hiddenSenderInfo->emptyUserpic.paint(
p, p,
@ -798,7 +800,8 @@ void RowPainter::paint(
int fullWidth, int fullWidth,
bool active, bool active,
bool selected, bool selected,
crl::time ms) { crl::time ms,
bool paused) {
const auto entry = row->entry(); const auto entry = row->entry();
const auto history = row->history(); const auto history = row->history();
const auto peer = history ? history->peer.get() : nullptr; const auto peer = history ? history->peer.get() : nullptr;
@ -868,7 +871,8 @@ void RowPainter::paint(
| (selected ? Flag::Selected : Flag(0)) | (selected ? Flag::Selected : Flag(0))
| (allowUserOnline ? Flag::AllowUserOnline : Flag(0)) | (allowUserOnline ? Flag::AllowUserOnline : Flag(0))
| (peer && peer->isSelf() ? Flag::SavedMessages : Flag(0)) | (peer && peer->isSelf() ? Flag::SavedMessages : Flag(0))
| (peer && peer->isRepliesChat() ? Flag::RepliesMessages : Flag(0)); | (peer && peer->isRepliesChat() ? Flag::RepliesMessages : Flag(0))
| (paused ? Flag::VideoPaused : Flag(0));
const auto paintItemCallback = [&](int nameleft, int namewidth) { const auto paintItemCallback = [&](int nameleft, int namewidth) {
const auto texttop = st::dialogsPadding.y() const auto texttop = st::dialogsPadding.y()
+ st::msgNameFont->height + st::msgNameFont->height

View file

@ -37,7 +37,8 @@ public:
int fullWidth, int fullWidth,
bool active, bool active,
bool selected, bool selected,
crl::time ms); crl::time ms,
bool paused);
static void paint( static void paint(
Painter &p, Painter &p,
not_null<const FakeRow*> row, not_null<const FakeRow*> row,

View file

@ -33,7 +33,8 @@ void VideoUserpic::paintLeft(
int x, int x,
int y, int y,
int w, int w,
int size) { int size,
bool paused) {
_lastSize = size; _lastSize = size;
const auto photoId = _peer->userpicPhotoId(); const auto photoId = _peer->userpicPhotoId();
@ -76,11 +77,8 @@ void VideoUserpic::paintLeft(
if (_video && _video->ready()) { if (_video && _video->ready()) {
startReady(); startReady();
const auto now = crl::now(); const auto now = paused ? crl::time(0) : crl::now();
p.drawPixmap( p.drawPixmap(x, y, _video->current(request(size), now));
x,
y,
_video->current(request(size), now));
} else { } else {
_peer->paintUserpicLeft(p, view, x, y, w, size); _peer->paintUserpicLeft(p, view, x, y, w, size);
} }
@ -122,4 +120,21 @@ void VideoUserpic::clipCallback(Media::Clip::Notification notification) {
} }
} }
void PaintUserpic(
Painter &p,
not_null<PeerData*> peer,
Ui::VideoUserpic *videoUserpic,
std::shared_ptr<Data::CloudImageView> &view,
int x,
int y,
int outerWidth,
int size,
bool paused) {
if (videoUserpic) {
videoUserpic->paintLeft(p, view, x, y, outerWidth, size, paused);
} else {
peer->paintUserpicLeft(p, view, x, y, outerWidth, size);
}
}
} // namespace Dialogs::Ui } // namespace Dialogs::Ui

View file

@ -33,7 +33,8 @@ public:
int x, int x,
int y, int y,
int w, int w,
int size); int size,
bool paused);
private: private:
void clipCallback(Media::Clip::Notification notification); void clipCallback(Media::Clip::Notification notification);
@ -51,4 +52,15 @@ private:
}; };
void PaintUserpic(
Painter &p,
not_null<PeerData*> peer,
Ui::VideoUserpic *videoUserpic,
std::shared_ptr<Data::CloudImageView> &view,
int x,
int y,
int outerWidth,
int size,
bool paused);
} // namespace Dialogs::Ui } // namespace Dialogs::Ui

View file

@ -84,6 +84,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_changes.h" #include "data/data_changes.h"
#include "data/stickers/data_stickers.h" #include "data/stickers/data_stickers.h"
#include "data/data_sponsored_messages.h" #include "data/data_sponsored_messages.h"
#include "dialogs/ui/dialogs_video_userpic.h"
#include "facades.h" #include "facades.h"
#include "styles/style_chat.h" #include "styles/style_chat.h"
#include "styles/style_window.h" // st::windowMinWidth #include "styles/style_window.h" // st::windowMinWidth
@ -367,8 +368,7 @@ HistoryInner::HistoryInner(
setMouseTracking(true); setMouseTracking(true);
_controller->gifPauseLevelChanged( _controller->gifPauseLevelChanged(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
if (!_controller->isGifPausedAtLeastFor( if (!elementIsGifPaused()) {
Window::GifPauseReason::Any)) {
update(); update();
} }
}, lifetime()); }, lifetime());
@ -1079,6 +1079,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
p.translate(0, -top); p.translate(0, -top);
} }
const auto paused = elementIsGifPaused();
enumerateUserpics([&](not_null<Element*> view, int userpicTop) { enumerateUserpics([&](not_null<Element*> view, int userpicTop) {
// stop the enumeration if the userpic is below the painted rect // stop the enumeration if the userpic is below the painted rect
if (userpicTop >= clip.top() + clip.height()) { if (userpicTop >= clip.top() + clip.height()) {
@ -1088,13 +1089,16 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
// paint the userpic if it intersects the painted rect // paint the userpic if it intersects the painted rect
if (userpicTop + st::msgPhotoSize > clip.top()) { if (userpicTop + st::msgPhotoSize > clip.top()) {
if (const auto from = view->data()->displayFrom()) { if (const auto from = view->data()->displayFrom()) {
from->paintUserpicLeft( Dialogs::Ui::PaintUserpic(
p, p,
from,
validateVideoUserpic(from),
_userpics[from], _userpics[from],
st::historyPhotoLeft, st::historyPhotoLeft,
userpicTop, userpicTop,
width(), width(),
st::msgPhotoSize); st::msgPhotoSize,
paused);
} else if (const auto info = view->data()->hiddenSenderInfo()) { } else if (const auto info = view->data()->hiddenSenderInfo()) {
if (info->customUserpic.empty()) { if (info->customUserpic.empty()) {
info->emptyUserpic.paint( info->emptyUserpic.paint(
@ -1200,6 +1204,46 @@ bool HistoryInner::eventHook(QEvent *e) {
return RpWidget::eventHook(e); return RpWidget::eventHook(e);
} }
HistoryInner::VideoUserpic *HistoryInner::validateVideoUserpic(
not_null<PeerData*> peer) {
if (!peer->isPremium()
|| peer->userpicPhotoUnknown()
|| !peer->userpicHasVideo()) {
_videoUserpics.remove(peer);
return nullptr;
}
const auto i = _videoUserpics.find(peer);
if (i != end(_videoUserpics)) {
return i->second.get();
}
const auto repaint = [=] {
enumerateUserpics([&](not_null<Element*> view, int userpicTop) {
// stop the enumeration if the userpic is below the painted rect
if (userpicTop >= _visibleAreaBottom) {
return false;
}
// repaint the userpic if it intersects the painted rect
if (userpicTop + st::msgPhotoSize > _visibleAreaTop) {
if (const auto from = view->data()->displayFrom()) {
if (from == peer) {
rtlupdate(
st::historyPhotoLeft,
userpicTop,
st::msgPhotoSize,
st::msgPhotoSize);
}
}
}
return true;
});
};
return _videoUserpics.emplace(peer, std::make_unique<VideoUserpic>(
peer,
repaint
)).first->second.get();
}
void HistoryInner::onTouchScrollTimer() { void HistoryInner::onTouchScrollTimer() {
auto nowTime = crl::now(); auto nowTime = crl::now();
if (_touchScrollState == Ui::TouchScrollState::Acceleration && _touchWaitingAcceleration && (nowTime - _touchAccelerationTime) > 40) { if (_touchScrollState == Ui::TouchScrollState::Acceleration && _touchWaitingAcceleration && (nowTime - _touchAccelerationTime) > 40) {

View file

@ -49,6 +49,10 @@ struct ChatPaintContext;
class PathShiftGradient; class PathShiftGradient;
} // namespace Ui } // namespace Ui
namespace Dialogs::Ui {
class VideoUserpic;
} // namespace Dialogs::Ui
class HistoryInner; class HistoryInner;
class HistoryMainElementDelegate; class HistoryMainElementDelegate;
class HistoryMainElementDelegateMixin { class HistoryMainElementDelegateMixin {
@ -209,6 +213,7 @@ private:
void onTouchScrollTimer(); void onTouchScrollTimer();
class BotAbout; class BotAbout;
using VideoUserpic = Dialogs::Ui::VideoUserpic;
using SelectedItems = std::map<HistoryItem*, TextSelection, std::less<>>; using SelectedItems = std::map<HistoryItem*, TextSelection, std::less<>>;
enum class MouseAction { enum class MouseAction {
None, None,
@ -387,6 +392,8 @@ private:
bool showCopyRestrictionForSelected(); bool showCopyRestrictionForSelected();
[[nodiscard]] bool hasSelectRestriction() const; [[nodiscard]] bool hasSelectRestriction() const;
VideoUserpic *validateVideoUserpic(not_null<PeerData*> peer);
// Does any of the shown histories has this flag set. // Does any of the shown histories has this flag set.
bool hasPendingResizedItems() const; bool hasPendingResizedItems() const;
@ -434,6 +441,9 @@ private:
base::flat_map< base::flat_map<
MsgId, MsgId,
std::shared_ptr<Data::CloudImageView>> _sponsoredUserpics; std::shared_ptr<Data::CloudImageView>> _sponsoredUserpics;
base::flat_map<
not_null<PeerData*>,
std::unique_ptr<VideoUserpic>> _videoUserpics;
std::unique_ptr<HistoryView::Reactions::Manager> _reactionsManager; std::unique_ptr<HistoryView::Reactions::Manager> _reactionsManager;