Added support of switching between corner layers in dialogs list.

This commit is contained in:
23rd 2022-12-01 18:11:50 +03:00
parent 34f0aae418
commit cbbbcd877c
2 changed files with 115 additions and 38 deletions

View file

@ -27,6 +27,63 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Dialogs {
constexpr auto kTopLayer = 1;
constexpr auto kNoneLayer = 0;
Row::CornerLayersManager::CornerLayersManager() = default;
bool Row::CornerLayersManager::isSameLayer(Layer layer) const {
return isFinished() && (_nextLayer == layer);
}
void Row::CornerLayersManager::setLayer(
Layer layer,
Fn<void()> updateCallback) {
if (_nextLayer == layer) {
return;
}
_lastFrameShown = false;
_prevLayer = _nextLayer;
_nextLayer = layer;
if (_animation.animating()) {
_animation.change(
1.,
st::dialogsOnlineBadgeDuration * (1. - _animation.value(1.)));
} else if (updateCallback) {
_animation.start(
std::move(updateCallback),
0.,
1.,
st::dialogsOnlineBadgeDuration);
}
}
float64 Row::CornerLayersManager::progressForLayer(Layer layer) const {
return (_nextLayer == layer)
? progress()
: (_prevLayer == layer)
? (1. - progress())
: 0.;
}
float64 Row::CornerLayersManager::progress() const {
return _animation.value(1.);
}
bool Row::CornerLayersManager::isFinished() const {
return (progress() == 1.) && _lastFrameShown;
}
void Row::CornerLayersManager::markFrameShown() {
if (progress() == 1.) {
_lastFrameShown = true;
}
}
bool Row::CornerLayersManager::isDisplayedNone() const {
return (progress() == 1.) && (_nextLayer == 0);
}
BasicRow::BasicRow() = default;
BasicRow::~BasicRow() = default;
@ -123,28 +180,25 @@ uint64 Row::sortKey(FilterId filterId) const {
}
void Row::setCornerBadgeShown(
bool shown,
CornerLayersManager::Layer nextLayer,
Fn<void()> updateCallback) const {
const auto value = (shown ? 1 : 0);
if (_cornerBadgeShown == value) {
return;
}
const_cast<Row*>(this)->_cornerBadgeShown = value;
if (_cornerBadgeUserpic && _cornerBadgeUserpic->animation.animating()) {
_cornerBadgeUserpic->animation.change(
_cornerBadgeShown ? 1. : 0.,
st::dialogsOnlineBadgeDuration);
} else if (updateCallback) {
ensureCornerBadgeUserpic();
_cornerBadgeUserpic->animation.start(
std::move(updateCallback),
_cornerBadgeShown ? 0. : 1.,
_cornerBadgeShown ? 1. : 0.,
st::dialogsOnlineBadgeDuration);
const auto cornerBadgeShown = (nextLayer ? 1 : 0);
if (_cornerBadgeShown == cornerBadgeShown) {
if (!cornerBadgeShown) {
return;
} else if (_cornerBadgeUserpic
&& _cornerBadgeUserpic->layersManager.isSameLayer(nextLayer)) {
return;
}
}
const_cast<Row*>(this)->_cornerBadgeShown = cornerBadgeShown;
ensureCornerBadgeUserpic();
_cornerBadgeUserpic->layersManager.setLayer(
nextLayer,
std::move(updateCallback));
if (!_cornerBadgeShown
&& _cornerBadgeUserpic
&& !_cornerBadgeUserpic->animation.animating()) {
&& _cornerBadgeUserpic->layersManager.isDisplayedNone()) {
_cornerBadgeUserpic = nullptr;
}
}
@ -154,16 +208,17 @@ void Row::updateCornerBadgeShown(
Fn<void()> updateCallback) const {
const auto user = peer->asUser();
const auto now = user ? base::unixtime::now() : TimeId();
const auto shown = [&] {
if (user) {
return Data::IsUserOnline(user, now);
} else if (const auto channel = peer->asChannel()) {
return Data::ChannelHasActiveCall(channel);
const auto nextLayer = [&] {
if (user && Data::IsUserOnline(user, now)) {
return kTopLayer;
} else if (peer->isChannel()
&& Data::ChannelHasActiveCall(peer->asChannel())) {
return kTopLayer;
}
return false;
return kNoneLayer;
}();
setCornerBadgeShown(shown, std::move(updateCallback));
if (shown && user) {
setCornerBadgeShown(nextLayer, std::move(updateCallback));
if ((nextLayer == kTopLayer) && user) {
peer->owner().watchForOffline(user, now);
}
}
@ -198,6 +253,7 @@ void Row::PaintCornerBadgeFrame(
PainterHighQualityEnabler hq(q);
q.setCompositionMode(QPainter::CompositionMode_Source);
const auto progress = data->layersManager.progressForLayer(kTopLayer);
const auto size = peer->isUser()
? st::dialogsOnlineBadgeSize
: st::dialogsCallBadgeSize;
@ -205,10 +261,10 @@ void Row::PaintCornerBadgeFrame(
const auto skip = peer->isUser()
? st::dialogsOnlineBadgeSkip
: st::dialogsCallBadgeSkip;
const auto shrink = (size / 2) * (1. - data->shown);
const auto shrink = (size / 2) * (1. - progress);
auto pen = QPen(Qt::transparent);
pen.setWidthF(stroke * data->shown);
pen.setWidthF(stroke * progress);
q.setPen(pen);
q.setBrush(data->active
? st::dialogsOnlineBadgeFgActive
@ -229,10 +285,10 @@ void Row::paintUserpic(
const Ui::PaintContext &context) const {
updateCornerBadgeShown(peer);
const auto shown = _cornerBadgeUserpic
? _cornerBadgeUserpic->animation.value(_cornerBadgeShown ? 1. : 0.)
: (_cornerBadgeShown ? 1. : 0.);
if (!historyForCornerBadge || shown == 0.) {
const auto cornerBadgeShown = !_cornerBadgeUserpic
? _cornerBadgeShown
: !_cornerBadgeUserpic->layersManager.isDisplayedNone();
if (!historyForCornerBadge || !cornerBadgeShown) {
BasicRow::paintUserpic(
p,
peer,
@ -261,15 +317,15 @@ void Row::paintUserpic(
}
const auto key = peer->userpicUniqueKey(userpicView());
const auto frameIndex = videoUserpic ? videoUserpic->frameIndex() : -1;
if (_cornerBadgeUserpic->shown != shown
if (!_cornerBadgeUserpic->layersManager.isFinished()
|| _cornerBadgeUserpic->key != key
|| _cornerBadgeUserpic->active != context.active
|| _cornerBadgeUserpic->frameIndex != frameIndex
|| videoUserpic) {
_cornerBadgeUserpic->shown = shown;
_cornerBadgeUserpic->key = key;
_cornerBadgeUserpic->active = context.active;
_cornerBadgeUserpic->frameIndex = frameIndex;
_cornerBadgeUserpic->layersManager.markFrameShown();
PaintCornerBadgeFrame(
_cornerBadgeUserpic.get(),
peer,
@ -290,7 +346,8 @@ void Row::paintUserpic(
: st::dialogsBg;
const auto size = st::dialogsCallBadgeSize;
const auto skip = st::dialogsCallBadgeSkip;
p.setOpacity(shown);
p.setOpacity(
_cornerBadgeUserpic->layersManager.progressForLayer(kTopLayer));
p.translate(context.st->padding.left(), context.st->padding.top());
actionPainter->paintSpeaking(
p,

View file

@ -143,17 +143,37 @@ public:
private:
friend class List;
class CornerLayersManager {
public:
using Layer = int;
CornerLayersManager();
[[nodiscard]] bool isSameLayer(Layer layer) const;
[[nodiscard]] bool isDisplayedNone() const;
[[nodiscard]] float64 progressForLayer(Layer layer) const;
[[nodiscard]] float64 progress() const;
[[nodiscard]] bool isFinished() const;
void setLayer(Layer layer, Fn<void()> updateCallback);
void markFrameShown();
private:
bool _lastFrameShown = false;
Layer _prevLayer = 0;
Layer _nextLayer = 0;
Ui::Animations::Simple _animation;
};
struct CornerBadgeUserpic {
InMemoryKey key;
float64 shown = 0.;
CornerLayersManager layersManager;
int frameIndex = -1;
bool active = false;
QImage frame;
Ui::Animations::Simple animation;
};
void setCornerBadgeShown(
bool shown,
CornerLayersManager::Layer nextLayer,
Fn<void()> updateCallback) const;
void ensureCornerBadgeUserpic() const;
static void PaintCornerBadgeFrame(