Browse Source

Update lottie icons in voice chats.

pull/16125/head
John Preston 8 months ago
parent
commit
4d91ab7079
  1. 3
      .gitattributes
  2. 1
      Telegram/Resources/icons/calls/active_hand.json
  3. 1
      Telegram/Resources/icons/calls/hand_muted_active.json
  4. 1
      Telegram/Resources/icons/calls/hands.lottie
  5. 1
      Telegram/Resources/icons/calls/raised_hand.json
  6. 1
      Telegram/Resources/icons/calls/voice.lottie
  7. 5
      Telegram/Resources/qrc/telegram/telegram.qrc
  8. 4
      Telegram/SourceFiles/calls/calls.style
  9. 7
      Telegram/SourceFiles/calls/calls_group_call.cpp
  10. 1
      Telegram/SourceFiles/calls/calls_group_call.h
  11. 8
      Telegram/SourceFiles/calls/calls_group_panel.cpp
  12. 238
      Telegram/SourceFiles/ui/controls/call_mute_button.cpp
  13. 18
      Telegram/SourceFiles/ui/controls/call_mute_button.h

3
.gitattributes

@ -4,3 +4,6 @@
# Ensure diffs have LF endings
*.diff text eol=lf
*.bat text eol=crlf
# Ensure lottie animations are treated as binary files
*.lottie binary

1
Telegram/Resources/icons/calls/active_hand.json
File diff suppressed because it is too large
View File

1
Telegram/Resources/icons/calls/hand_muted_active.json
File diff suppressed because it is too large
View File

1
Telegram/Resources/icons/calls/hands.lottie
File diff suppressed because it is too large
View File

1
Telegram/Resources/icons/calls/raised_hand.json
File diff suppressed because it is too large
View File

1
Telegram/Resources/icons/calls/voice.lottie
File diff suppressed because it is too large
View File

5
Telegram/Resources/qrc/telegram/telegram.qrc

@ -59,9 +59,8 @@
<file alias="day-blue.tdesktop-theme">../../day-blue.tdesktop-theme</file>
<file alias="night.tdesktop-theme">../../night.tdesktop-theme</file>
<file alias="night-green.tdesktop-theme">../../night-green.tdesktop-theme</file>
<file alias="icons/calls/active_hand.json">../../icons/calls/active_hand.json</file>
<file alias="icons/calls/hand_muted_active.json">../../icons/calls/hand_muted_active.json</file>
<file alias="icons/calls/raised_hand.json">../../icons/calls/raised_hand.json</file>
<file alias="icons/calls/hands.lottie">../../icons/calls/hands.lottie</file>
<file alias="icons/calls/voice.lottie">../../icons/calls/voice.lottie</file>
</qresource>
<qresource prefix="/qt-project.org">
<file>../qmime/freedesktop.org.xml</file>

4
Telegram/SourceFiles/calls/calls.style

@ -393,8 +393,8 @@ callErrorToast: Toast(defaultToast) {
groupCallWidth: 380px;
groupCallHeight: 580px;
groupCallMuteButtonIconSize: size(55px, 55px);
groupCallMuteButtonIconTop: 42px;
groupCallMuteButtonIconSize: size(69px, 69px);
groupCallMuteButtonIconTop: 35px;
groupCallRipple: RippleAnimation(defaultRippleAnimation) {
color: groupCallMembersBgRipple;
}

7
Telegram/SourceFiles/calls/calls_group_call.cpp

@ -357,6 +357,13 @@ bool GroupCall::showChooseJoinAs() const {
&& !_possibleJoinAs.front()->isSelf());
}
bool GroupCall::scheduleStartSubscribed() const {
if (const auto real = lookupReal()) {
return real->scheduleStartSubscribed();
}
return false;
}
Data::GroupCall *GroupCall::lookupReal() const {
const auto real = _peer->groupCall();
return (real && real->id() == _id) ? real : nullptr;

1
Telegram/SourceFiles/calls/calls_group_call.h

@ -113,6 +113,7 @@ public:
[[nodiscard]] TimeId scheduleDate() const {
return _scheduleDate;
}
[[nodiscard]] bool scheduleStartSubscribed() const;
[[nodiscard]] Data::GroupCall *lookupReal() const;
[[nodiscard]] rpl::producer<not_null<Data::GroupCall*>> real() const;

8
Telegram/SourceFiles/calls/calls_group_panel.cpp

@ -388,9 +388,13 @@ Panel::Panel(not_null<GroupCall*> call)
.text = (_call->scheduleDate()
? tr::lng_group_call_start_now(tr::now)
: tr::lng_group_call_connecting(tr::now)),
.type = (_call->scheduleDate()
.type = (!_call->scheduleDate()
? Ui::CallMuteButtonType::Connecting
: _peer->canManageGroupCall()
? Ui::CallMuteButtonType::ScheduledCanStart
: Ui::CallMuteButtonType::Connecting),
: _call->scheduleStartSubscribed()
? Ui::CallMuteButtonType::ScheduledNotify
: Ui::CallMuteButtonType::ScheduledSilent),
}))
, _hangup(widget(), st::groupCallHangup) {
_layerBg->setStyleOverrides(&st::groupCallBox, &st::groupCallLayerBox);

238
Telegram/SourceFiles/ui/controls/call_mute_button.cpp

@ -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,
.color = st::groupCallIconFg,
.sizeOverride = st::groupCallMuteButtonIconSize,
.frame = 0,
});
_icons[2].emplace(Lottie::IconDescriptor{
.path = u":/gui/icons/calls/raised_hand.json"_q,
.path = u":/gui/icons/calls/hands.lottie"_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);
}
}

18
Telegram/SourceFiles/ui/controls/call_mute_button.h

@ -87,20 +87,25 @@ private:
const style::InfiniteRadialAnimation &st = st::callConnectingRadial;
};
struct IconState {
not_null<Lottie::Icon*> icon;
int index = -1;
int frameFrom = 0;
int frameTo = 0;
std::optional<int> otherJumpToFrame;
inline bool operator==(const IconState &other) const {
return (icon == other.icon)
return (index == other.index)
&& (frameFrom == other.frameFrom)
&& (frameTo == other.frameTo)
&& (otherJumpToFrame == other.otherJumpToFrame);
&& (frameTo == other.frameTo);
}
inline bool operator!=(const IconState &other) const {
return !(*this == other);
}
bool valid() const {
return (index >= 0);
}
explicit operator bool() const {
return valid();
}
};
void init();
@ -118,6 +123,7 @@ private:
[[nodiscard]] IconState initialState();
[[nodiscard]] IconState iconStateFrom(CallMuteButtonType previous);
[[nodiscard]] IconState randomWavingState();
[[nodiscard]] IconState iconStateAnimated(CallMuteButtonType previous);
void scheduleIconState(const IconState &state);
void startIconState(const IconState &state);
void iconAnimationCallback();
@ -143,7 +149,7 @@ private:
std::unique_ptr<InfiniteRadialAnimation> _radial;
const base::flat_map<CallMuteButtonType, anim::gradient_colors> _colors;
std::array<std::optional<Lottie::Icon>, 3> _icons;
std::array<std::optional<Lottie::Icon>, 2> _icons;
IconState _iconState;
std::optional<IconState> _scheduledState;

Loading…
Cancel
Save