mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/telegramdesktop/tdesktop
synced 2026-09-20 08:03:45 +08:00
Update lottie icons in voice chats.
This commit is contained in:
@@ -70,6 +70,14 @@ constexpr auto kOverlapProgressRadialHide = 1.2;
|
||||
|
||||
constexpr auto kRadialFinishArcShift = 1200;
|
||||
|
||||
[[nodiscard]] CallMuteButtonType TypeForIcon(CallMuteButtonType type) {
|
||||
return (type == CallMuteButtonType::Connecting)
|
||||
? CallMuteButtonType::Muted
|
||||
: (type == CallMuteButtonType::RaisedHand)
|
||||
? CallMuteButtonType::ForceMuted
|
||||
: type;
|
||||
};
|
||||
|
||||
auto MuteBlobs() {
|
||||
return std::vector<Paint::Blobs::BlobData>{
|
||||
{
|
||||
@@ -546,160 +554,101 @@ CallMuteButton::CallMuteButton(
|
||||
}
|
||||
|
||||
CallMuteButton::IconState CallMuteButton::initialState() {
|
||||
const auto result = iconStateFrom(_state.current().type);
|
||||
_icons[0].emplace(Lottie::IconDescriptor{
|
||||
.path = u":/gui/icons/calls/hand_muted_active.json"_q,
|
||||
.path = u":/gui/icons/calls/voice.lottie"_q,
|
||||
.color = st::groupCallIconFg,
|
||||
.sizeOverride = st::groupCallMuteButtonIconSize,
|
||||
.frame = 22,
|
||||
.frame = result.frameTo,
|
||||
});
|
||||
_icons[1].emplace(Lottie::IconDescriptor{
|
||||
.path = u":/gui/icons/calls/active_hand.json"_q,
|
||||
.path = u":/gui/icons/calls/hands.lottie"_q,
|
||||
.color = st::groupCallIconFg,
|
||||
.sizeOverride = st::groupCallMuteButtonIconSize,
|
||||
.frame = 0,
|
||||
});
|
||||
_icons[2].emplace(Lottie::IconDescriptor{
|
||||
.path = u":/gui/icons/calls/raised_hand.json"_q,
|
||||
.color = st::groupCallIconFg,
|
||||
.sizeOverride = st::groupCallMuteButtonIconSize,
|
||||
.frame = 0,
|
||||
});
|
||||
return iconStateFrom(_state.current().type);
|
||||
return result;
|
||||
}
|
||||
|
||||
auto CallMuteButton::iconStateAnimated(CallMuteButtonType previous)
|
||||
-> IconState {
|
||||
using Type = CallMuteButtonType;
|
||||
using Key = std::pair<Type, Type>;
|
||||
struct Animation {
|
||||
int from = 0;
|
||||
int to = 0;
|
||||
};
|
||||
static const auto kAnimations = std::vector<std::pair<Key, Animation>>{
|
||||
{ { Type::ForceMuted, Type::Muted }, { 0, 35 } },
|
||||
{ { Type::Muted, Type::Active }, { 36, 68 } },
|
||||
{ { Type::Active, Type::Muted }, { 69, 98 } },
|
||||
{ { Type::Muted, Type::ForceMuted }, { 99, 135 } },
|
||||
{ { Type::Active, Type::ForceMuted }, { 136, 172 } },
|
||||
{ { Type::ScheduledSilent, Type::ScheduledNotify }, { 173, 201 } },
|
||||
{ { Type::ScheduledSilent, Type::Muted }, { 202, 236 } },
|
||||
{ { Type::ScheduledSilent, Type::ForceMuted }, { 237, 273 } },
|
||||
{ { Type::ScheduledNotify, Type::ForceMuted }, { 274, 310 } },
|
||||
{ { Type::ScheduledNotify, Type::ScheduledSilent }, { 311, 343 } },
|
||||
{ { Type::ScheduledNotify, Type::Muted }, { 344, 375 } },
|
||||
{ { Type::ScheduledCanStart, Type::Muted }, { 376, 403 } },
|
||||
};
|
||||
static const auto kMap = [] {
|
||||
// flat_multi_map_pair_type lacks some required constructors :(
|
||||
auto &&list = kAnimations | ranges::views::transform([](auto &&pair) {
|
||||
return base::flat_multi_map_pair_type<Key, Animation>(
|
||||
pair.first,
|
||||
pair.second);
|
||||
});
|
||||
return base::flat_map<Key, Animation>(begin(list), end(list));
|
||||
}();
|
||||
const auto was = TypeForIcon(previous);
|
||||
const auto now = TypeForIcon(_state.current().type);
|
||||
if (was == now) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (const auto i = kMap.find(Key{ was, now }); i != end(kMap)) {
|
||||
return { 0, i->second.from, i->second.to };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
CallMuteButton::IconState CallMuteButton::iconStateFrom(
|
||||
CallMuteButtonType previous) {
|
||||
const auto current = _state.current().type;
|
||||
switch (previous) {
|
||||
case CallMuteButtonType::Active: {
|
||||
switch (current) {
|
||||
case CallMuteButtonType::Active: {
|
||||
return { // Active
|
||||
.icon = &*_icons[0],
|
||||
.frameFrom = 41,
|
||||
.frameTo = 41,
|
||||
.otherJumpToFrame = 0,
|
||||
};
|
||||
} break;
|
||||
case CallMuteButtonType::Connecting:
|
||||
case CallMuteButtonType::Muted: {
|
||||
return { // Active -> Muted
|
||||
.icon = &*_icons[0],
|
||||
.frameFrom = 42,
|
||||
.frameTo = 62,
|
||||
};
|
||||
} break;
|
||||
case CallMuteButtonType::ScheduledCanStart: // #TODO voice chats
|
||||
case CallMuteButtonType::ScheduledNotify:
|
||||
case CallMuteButtonType::ScheduledSilent:
|
||||
case CallMuteButtonType::ForceMuted:
|
||||
case CallMuteButtonType::RaisedHand: {
|
||||
return { // Active -> Hand
|
||||
.icon = &*_icons[1],
|
||||
.frameFrom = 0,
|
||||
.frameTo = 22,
|
||||
.otherJumpToFrame = 0,
|
||||
};
|
||||
} break;
|
||||
}
|
||||
} break;
|
||||
case CallMuteButtonType::Connecting:
|
||||
case CallMuteButtonType::Muted: {
|
||||
switch (current) {
|
||||
case CallMuteButtonType::Active: {
|
||||
return { // Muted -> Active
|
||||
.icon = &*_icons[0],
|
||||
.frameFrom = 21,
|
||||
.frameTo = 41,
|
||||
.otherJumpToFrame = 0,
|
||||
};
|
||||
} break;
|
||||
case CallMuteButtonType::Connecting:
|
||||
case CallMuteButtonType::Muted: {
|
||||
return { // Muted
|
||||
.icon = &*_icons[0],
|
||||
.frameFrom = 22,
|
||||
.frameTo = 22,
|
||||
};
|
||||
} break;
|
||||
case CallMuteButtonType::ScheduledCanStart: // #TODO voice chats
|
||||
case CallMuteButtonType::ScheduledNotify:
|
||||
case CallMuteButtonType::ScheduledSilent:
|
||||
case CallMuteButtonType::ForceMuted:
|
||||
case CallMuteButtonType::RaisedHand: {
|
||||
return { // Muted -> Hand
|
||||
.icon = &*_icons[0],
|
||||
.frameFrom = 63,
|
||||
.frameTo = 83,
|
||||
.otherJumpToFrame = 22,
|
||||
};
|
||||
} break;
|
||||
}
|
||||
} break;
|
||||
case CallMuteButtonType::ScheduledCanStart: // #TODO voice chats
|
||||
case CallMuteButtonType::ScheduledNotify:
|
||||
case CallMuteButtonType::ScheduledSilent:
|
||||
case CallMuteButtonType::ForceMuted:
|
||||
case CallMuteButtonType::RaisedHand: {
|
||||
switch (current) {
|
||||
case CallMuteButtonType::Active: {
|
||||
return { // Hand -> Active
|
||||
.icon = &*_icons[1],
|
||||
.frameFrom = 22,
|
||||
.frameTo = 0,
|
||||
.otherJumpToFrame = 42,
|
||||
};
|
||||
} break;
|
||||
case CallMuteButtonType::Connecting:
|
||||
case CallMuteButtonType::Muted: {
|
||||
return { // Hand -> Muted
|
||||
.icon = &*_icons[0],
|
||||
.frameFrom = 0,
|
||||
.frameTo = 20,
|
||||
};
|
||||
} break;
|
||||
case CallMuteButtonType::ScheduledCanStart: // #TODO voice chats
|
||||
case CallMuteButtonType::ScheduledNotify:
|
||||
case CallMuteButtonType::ScheduledSilent:
|
||||
case CallMuteButtonType::ForceMuted:
|
||||
case CallMuteButtonType::RaisedHand: {
|
||||
return { // Hand
|
||||
.icon = &*_icons[0],
|
||||
.frameFrom = 0,
|
||||
.frameTo = 0,
|
||||
.otherJumpToFrame = 22,
|
||||
};
|
||||
} break;
|
||||
}
|
||||
} break;
|
||||
if (const auto animated = iconStateAnimated(previous)) {
|
||||
return animated;
|
||||
}
|
||||
Unexpected("State in CallMuteButton::iconStateFrom.");
|
||||
|
||||
using Type = CallMuteButtonType;
|
||||
static const auto kFinal = base::flat_map<Type, int>{
|
||||
{ Type::ForceMuted, 0 },
|
||||
{ Type::Muted, 36 },
|
||||
{ Type::Active, 69 },
|
||||
{ Type::ScheduledSilent, 173 },
|
||||
{ Type::ScheduledNotify, 274 },
|
||||
{ Type::ScheduledCanStart, 376 },
|
||||
};
|
||||
|
||||
const auto now = TypeForIcon(_state.current().type);
|
||||
const auto i = kFinal.find(now);
|
||||
|
||||
Ensures(i != end(kFinal));
|
||||
return { 0, i->second, i->second };
|
||||
}
|
||||
|
||||
CallMuteButton::IconState CallMuteButton::randomWavingState() {
|
||||
switch (openssl::RandomValue<uint32>() % 5) {
|
||||
case 0: return {
|
||||
.icon = &*_icons[2],
|
||||
.frameFrom = 0,
|
||||
.frameTo = 120 };
|
||||
case 1: return {
|
||||
.icon = &*_icons[2],
|
||||
.frameFrom = 120,
|
||||
.frameTo = 240 };
|
||||
case 2: return {
|
||||
.icon = &*_icons[2],
|
||||
.frameFrom = 240,
|
||||
.frameTo = 420 };
|
||||
case 3: return {
|
||||
.icon = &*_icons[2],
|
||||
.frameFrom = 420,
|
||||
.frameTo = 540 };
|
||||
case 4: return {
|
||||
.icon = &*_icons[2],
|
||||
.frameFrom = 540,
|
||||
.frameTo = 720 };
|
||||
}
|
||||
Unexpected("Value in CallMuteButton::randomWavingState.");
|
||||
struct Animation {
|
||||
int from = 0;
|
||||
int to = 0;
|
||||
};
|
||||
static const auto kAnimations = std::vector<Animation>{
|
||||
{ 0, 120 },
|
||||
{ 120, 240 },
|
||||
{ 240, 420 },
|
||||
{ 420, 540 },
|
||||
};
|
||||
const auto index = openssl::RandomValue<uint32>() % kAnimations.size();
|
||||
return { 1, kAnimations[index].from, kAnimations[index].to };
|
||||
}
|
||||
|
||||
void CallMuteButton::init() {
|
||||
@@ -884,7 +833,7 @@ void CallMuteButton::init() {
|
||||
) | rpl::start_with_next([=](QRect clip) {
|
||||
Painter p(_content);
|
||||
|
||||
_iconState.icon->paint(p, _muteIconRect.x(), _muteIconRect.y());
|
||||
_icons[_iconState.index]->paint(p, _muteIconRect.x(), _muteIconRect.y());
|
||||
|
||||
if (_radialInfo.state.has_value() && _switchAnimation.animating()) {
|
||||
const auto radialProgress = _radialInfo.realShowProgress;
|
||||
@@ -929,7 +878,7 @@ void CallMuteButton::init() {
|
||||
|
||||
void CallMuteButton::scheduleIconState(const IconState &state) {
|
||||
if (_iconState != state) {
|
||||
if (_iconState.icon->animating()) {
|
||||
if (_icons[_iconState.index]->animating()) {
|
||||
_scheduledState = state;
|
||||
} else {
|
||||
startIconState(state);
|
||||
@@ -942,22 +891,15 @@ void CallMuteButton::scheduleIconState(const IconState &state) {
|
||||
void CallMuteButton::startIconState(const IconState &state) {
|
||||
_iconState = state;
|
||||
_scheduledState = std::nullopt;
|
||||
_iconState.icon->animate(
|
||||
_icons[_iconState.index]->animate(
|
||||
[=] { iconAnimationCallback(); },
|
||||
_iconState.frameFrom,
|
||||
_iconState.frameTo);
|
||||
if (const auto other = state.otherJumpToFrame) {
|
||||
if (_iconState.icon == &*_icons[0]) {
|
||||
_icons[1]->jumpTo(*other, nullptr);
|
||||
} else {
|
||||
_icons[0]->jumpTo(*other, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CallMuteButton::iconAnimationCallback() {
|
||||
_content->update(_muteIconRect);
|
||||
if (!_iconState.icon->animating() && _scheduledState) {
|
||||
if (!_icons[_iconState.index]->animating() && _scheduledState) {
|
||||
startIconState(*_scheduledState);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user