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

View file

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

View file

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

View file

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

View file

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

View file

@ -33,7 +33,8 @@ void VideoUserpic::paintLeft(
int x,
int y,
int w,
int size) {
int size,
bool paused) {
_lastSize = size;
const auto photoId = _peer->userpicPhotoId();
@ -76,11 +77,8 @@ void VideoUserpic::paintLeft(
if (_video && _video->ready()) {
startReady();
const auto now = crl::now();
p.drawPixmap(
x,
y,
_video->current(request(size), now));
const auto now = paused ? crl::time(0) : crl::now();
p.drawPixmap(x, y, _video->current(request(size), now));
} else {
_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

View file

@ -33,7 +33,8 @@ public:
int x,
int y,
int w,
int size);
int size,
bool paused);
private:
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

View file

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

View file

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