|
|
|
@@ -10,7 +10,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|
|
|
|
#include "ui/chat/message_bar.h"
|
|
|
|
|
#include "ui/widgets/shadow.h"
|
|
|
|
|
#include "ui/widgets/buttons.h"
|
|
|
|
|
#include "ui/paint/blobs.h"
|
|
|
|
|
#include "lang/lang_keys.h"
|
|
|
|
|
#include "base/openssl_help.h"
|
|
|
|
|
#include "styles/style_chat.h"
|
|
|
|
|
#include "styles/style_calls.h"
|
|
|
|
|
#include "styles/style_info.h" // st::topBarArrowPadding, like TopBarWidget.
|
|
|
|
@@ -25,8 +27,66 @@ constexpr auto kDuration = 160;
|
|
|
|
|
constexpr auto kMaxUserpics = 4;
|
|
|
|
|
constexpr auto kWideScale = 5;
|
|
|
|
|
|
|
|
|
|
constexpr auto kBlobsEnterDuration = crl::time(250);
|
|
|
|
|
constexpr auto kLevelDuration = 100. + 500. * 0.23;
|
|
|
|
|
constexpr auto kBlobScale = 0.605;
|
|
|
|
|
constexpr auto kMinorBlobFactor = 0.9f;
|
|
|
|
|
constexpr auto kUserpicMinScale = 0.8;
|
|
|
|
|
constexpr auto kMaxLevel = 1.;
|
|
|
|
|
constexpr auto kSendRandomLevelInterval = crl::time(100);
|
|
|
|
|
|
|
|
|
|
auto Blobs()->std::array<Ui::Paint::Blobs::BlobData, 2> {
|
|
|
|
|
return { {
|
|
|
|
|
{
|
|
|
|
|
.segmentsCount = 6,
|
|
|
|
|
.minScale = kBlobScale * kMinorBlobFactor,
|
|
|
|
|
.minRadius = st::historyGroupCallBlobMinRadius * kMinorBlobFactor,
|
|
|
|
|
.maxRadius = st::historyGroupCallBlobMaxRadius * kMinorBlobFactor,
|
|
|
|
|
.speedScale = 1.,
|
|
|
|
|
.alpha = .5,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.segmentsCount = 8,
|
|
|
|
|
.minScale = kBlobScale,
|
|
|
|
|
.minRadius = (float)st::historyGroupCallBlobMinRadius,
|
|
|
|
|
.maxRadius = (float)st::historyGroupCallBlobMaxRadius,
|
|
|
|
|
.speedScale = 1.,
|
|
|
|
|
.alpha = .2,
|
|
|
|
|
},
|
|
|
|
|
} };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
struct GroupCallBar::BlobsAnimation {
|
|
|
|
|
BlobsAnimation(
|
|
|
|
|
std::vector<Ui::Paint::Blobs::BlobData> blobDatas,
|
|
|
|
|
float levelDuration,
|
|
|
|
|
float maxLevel)
|
|
|
|
|
: blobs(std::move(blobDatas), levelDuration, maxLevel) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ui::Paint::Blobs blobs;
|
|
|
|
|
crl::time lastTime = 0;
|
|
|
|
|
crl::time lastSpeakingUpdateTime = 0;
|
|
|
|
|
float64 enter = 0.;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct GroupCallBar::Userpic {
|
|
|
|
|
User data;
|
|
|
|
|
std::pair<uint64, uint64> cacheKey;
|
|
|
|
|
crl::time speakingStarted = 0;
|
|
|
|
|
QImage cache;
|
|
|
|
|
Animations::Simple leftAnimation;
|
|
|
|
|
Animations::Simple shownAnimation;
|
|
|
|
|
std::unique_ptr<BlobsAnimation> blobsAnimation;
|
|
|
|
|
int left = 0;
|
|
|
|
|
bool positionInited = false;
|
|
|
|
|
bool topMost = false;
|
|
|
|
|
bool hiding = false;
|
|
|
|
|
bool cacheMasked = false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
GroupCallBar::GroupCallBar(
|
|
|
|
|
not_null<QWidget*> parent,
|
|
|
|
|
rpl::producer<GroupCallBarContent> content)
|
|
|
|
@@ -36,7 +96,8 @@ GroupCallBar::GroupCallBar(
|
|
|
|
|
_inner.get(),
|
|
|
|
|
tr::lng_group_call_join(),
|
|
|
|
|
st::groupCallTopBarJoin))
|
|
|
|
|
, _shadow(std::make_unique<PlainShadow>(_wrap.parentWidget())) {
|
|
|
|
|
, _shadow(std::make_unique<PlainShadow>(_wrap.parentWidget()))
|
|
|
|
|
, _randomSpeakingTimer([=] { sendRandomLevels(); }) {
|
|
|
|
|
_wrap.hide(anim::type::instant);
|
|
|
|
|
_shadow->hide();
|
|
|
|
|
|
|
|
|
@@ -78,6 +139,28 @@ GroupCallBar::GroupCallBar(
|
|
|
|
|
_wrap.toggle(false, anim::type::normal);
|
|
|
|
|
}, lifetime());
|
|
|
|
|
|
|
|
|
|
style::PaletteChanged(
|
|
|
|
|
) | rpl::start_with_next([=] {
|
|
|
|
|
for (auto &userpic : _userpics) {
|
|
|
|
|
userpic.cache = QImage();
|
|
|
|
|
}
|
|
|
|
|
}, lifetime());
|
|
|
|
|
|
|
|
|
|
_speakingAnimation.init([=](crl::time now) {
|
|
|
|
|
//if (const auto &last = _speakingAnimationHideLastTime; (last > 0)
|
|
|
|
|
// && (now - last >= kBlobsEnterDuration)) {
|
|
|
|
|
// _speakingAnimation.stop();
|
|
|
|
|
// return false;
|
|
|
|
|
//}
|
|
|
|
|
for (auto &userpic : _userpics) {
|
|
|
|
|
if (const auto blobs = userpic.blobsAnimation.get()) {
|
|
|
|
|
blobs->blobs.updateLevel(now - blobs->lastTime);
|
|
|
|
|
blobs->lastTime = now;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
updateUserpics();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
setupInner();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -168,17 +251,30 @@ void GroupCallBar::paintUserpics(Painter &p) {
|
|
|
|
|
validateUserpicCache(userpic);
|
|
|
|
|
p.setOpacity(shown);
|
|
|
|
|
const auto left = middle + userpic.leftAnimation.value(userpic.left);
|
|
|
|
|
if (userpic.data.speaking) {
|
|
|
|
|
//p.fillRect(left, top, size, size, QColor(255, 128, 128));
|
|
|
|
|
const auto blobs = userpic.blobsAnimation.get();
|
|
|
|
|
const auto shownScale = 0.5 + shown / 2.;
|
|
|
|
|
const auto &minScale = kUserpicMinScale;
|
|
|
|
|
const auto scale = shownScale * (blobs
|
|
|
|
|
? (minScale + (1. - minScale) * blobs->blobs.currentLevel())
|
|
|
|
|
: 1.);
|
|
|
|
|
if (blobs) {
|
|
|
|
|
auto hq = PainterHighQualityEnabler(p);
|
|
|
|
|
|
|
|
|
|
const auto shift = QPointF(left + size / 2., top + size / 2.);
|
|
|
|
|
p.translate(shift);
|
|
|
|
|
blobs->blobs.paint(p, st::windowActiveTextFg);
|
|
|
|
|
p.translate(-shift);
|
|
|
|
|
p.setOpacity(1.);
|
|
|
|
|
}
|
|
|
|
|
if (shown == 1.) {
|
|
|
|
|
if (std::abs(scale - 1.) < 0.001) {
|
|
|
|
|
const auto skip = ((kWideScale - 1) / 2) * size * factor;
|
|
|
|
|
p.drawImage(
|
|
|
|
|
QRect(left, top, size, size),
|
|
|
|
|
userpic.cache,
|
|
|
|
|
QRect(skip, skip, size * factor, size * factor));
|
|
|
|
|
} else {
|
|
|
|
|
const auto scale = 0.5 + shown / 2.;
|
|
|
|
|
auto hq = PainterHighQualityEnabler(p);
|
|
|
|
|
|
|
|
|
|
auto target = QRect(
|
|
|
|
|
left + (1 - kWideScale) / 2 * size,
|
|
|
|
|
top + (1 - kWideScale) / 2 * size,
|
|
|
|
@@ -215,6 +311,26 @@ bool GroupCallBar::needUserpicCacheRefresh(Userpic &userpic) {
|
|
|
|
|
return !userpic.leftAnimation.animating();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GroupCallBar::ensureBlobsAnimation(Userpic &userpic) {
|
|
|
|
|
if (userpic.blobsAnimation) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
userpic.blobsAnimation = std::make_unique<BlobsAnimation>(
|
|
|
|
|
Blobs() | ranges::to_vector,
|
|
|
|
|
kLevelDuration,
|
|
|
|
|
kMaxLevel);
|
|
|
|
|
userpic.blobsAnimation->lastTime = crl::now();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GroupCallBar::sendRandomLevels() {
|
|
|
|
|
for (auto &userpic : _userpics) {
|
|
|
|
|
if (const auto blobs = userpic.blobsAnimation.get()) {
|
|
|
|
|
const auto value = 30 + (openssl::RandomValue<uint32>() % 70);
|
|
|
|
|
userpic.blobsAnimation->blobs.setLevel(float64(value) / 100.);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GroupCallBar::validateUserpicCache(Userpic &userpic) {
|
|
|
|
|
if (!needUserpicCacheRefresh(userpic)) {
|
|
|
|
|
return;
|
|
|
|
@@ -248,7 +364,6 @@ void GroupCallBar::validateUserpicCache(Userpic &userpic) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GroupCallBar::updateControlsGeometry(QRect wrapGeometry) {
|
|
|
|
|
_inner->resizeToWidth(wrapGeometry.width());
|
|
|
|
|
const auto hidden = _wrap.isHidden() || !wrapGeometry.height();
|
|
|
|
|
if (_shadow->isHidden() != hidden) {
|
|
|
|
|
_shadow->setVisible(!hidden);
|
|
|
|
@@ -308,8 +423,15 @@ void GroupCallBar::updateUserpicsFromContent() {
|
|
|
|
|
const auto userpicsBegin = begin(_userpics);
|
|
|
|
|
const auto userpicsEnd = end(_userpics);
|
|
|
|
|
auto markedTopMost = userpicsEnd;
|
|
|
|
|
auto hasBlobs = false;
|
|
|
|
|
for (auto i = userpicsBegin; i != userpicsEnd; ++i) {
|
|
|
|
|
auto &userpic = *i;
|
|
|
|
|
if (userpic.data.speaking) {
|
|
|
|
|
ensureBlobsAnimation(userpic);
|
|
|
|
|
hasBlobs = true;
|
|
|
|
|
} else {
|
|
|
|
|
userpic.blobsAnimation = nullptr;
|
|
|
|
|
}
|
|
|
|
|
if (userpic.topMost) {
|
|
|
|
|
toggleUserpic(userpic, false);
|
|
|
|
|
userpic.topMost = false;
|
|
|
|
@@ -323,6 +445,21 @@ void GroupCallBar::updateUserpicsFromContent() {
|
|
|
|
|
std::rotate(userpicsBegin, markedTopMost, markedTopMost + 1);
|
|
|
|
|
}
|
|
|
|
|
updateUserpicsPositions();
|
|
|
|
|
|
|
|
|
|
if (!hasBlobs) {
|
|
|
|
|
_randomSpeakingTimer.cancel();
|
|
|
|
|
_speakingAnimation.stop();
|
|
|
|
|
} else if (!_randomSpeakingTimer.isActive()) {
|
|
|
|
|
_randomSpeakingTimer.callEach(kSendRandomLevelInterval);
|
|
|
|
|
_speakingAnimation.start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_wrap.isHidden()) {
|
|
|
|
|
for (auto &userpic : _userpics) {
|
|
|
|
|
userpic.shownAnimation.stop();
|
|
|
|
|
userpic.leftAnimation.stop();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GroupCallBar::toggleUserpic(Userpic &userpic, bool shown) {
|
|
|
|
@@ -408,6 +545,7 @@ void GroupCallBar::move(int x, int y) {
|
|
|
|
|
|
|
|
|
|
void GroupCallBar::resizeToWidth(int width) {
|
|
|
|
|
_wrap.entity()->resizeToWidth(width);
|
|
|
|
|
_inner->resizeToWidth(width);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int GroupCallBar::height() const {
|
|
|
|
|