Add raised hand display in participants list.

This commit is contained in:
John Preston 2021-03-09 11:24:29 +04:00
parent fb579f1c10
commit 361e3565d4
11 changed files with 63 additions and 35 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 718 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1008 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -1923,6 +1923,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_group_call_title" = "Voice Chat";
"lng_group_call_active" = "speaking";
"lng_group_call_inactive" = "listening";
"lng_group_call_raised_hand_status" = "wants to speak";
"lng_group_call_settings" = "Settings";
"lng_group_call_unmute" = "Unmute";
"lng_group_call_unmute_sub" = "or hold spacebar to talk";

View File

@ -588,6 +588,7 @@ groupCallMemberColoredCrossLine: CrossLineAnimation(groupCallMemberInactiveCross
}
groupCallMemberInvited: icon {{ "calls/group_calls_invited", groupCallMemberInactiveIcon }};
groupCallMemberInvitedPosition: point(2px, 12px);
groupCallMemberRaisedHand: icon {{ "calls/group_calls_raised_hand", groupCallMemberInactiveStatus }};
groupCallSettings: CallButton(callMicrophoneMute) {
button: IconButton(callButton) {

View File

@ -79,16 +79,20 @@ class Row;
class RowDelegate {
public:
struct IconState {
float64 speaking = 0.;
float64 active = 0.;
float64 muted = 0.;
bool mutedByMe = false;
bool raisedHand = false;
};
virtual bool rowIsMe(not_null<PeerData*> participantPeer) = 0;
virtual bool rowCanMuteMembers() = 0;
virtual void rowUpdateRow(not_null<Row*> row) = 0;
virtual void rowPaintIcon(
Painter &p,
QRect rect,
float64 speaking,
float64 active,
float64 muted,
bool mutedByMe) = 0;
IconState state) = 0;
};
class Row final : public PeerListRow {
@ -101,6 +105,7 @@ public:
Active,
Inactive,
Muted,
RaisedHand,
MutedByMe,
Invited,
};
@ -281,10 +286,7 @@ public:
void rowPaintIcon(
Painter &p,
QRect rect,
float64 speaking,
float64 active,
float64 muted,
bool mutedByMe) override;
IconState state) override;
private:
[[nodiscard]] std::unique_ptr<Row> createRowForMe();
@ -383,14 +385,19 @@ void Row::updateState(const Data::GroupCall::Participant *participant) {
setSounding(participant->sounding && participant->ssrc != 0);
setSpeaking(participant->speaking && participant->ssrc != 0);
} else if (participant->canSelfUnmute) {
setState(participant->mutedByMe ? State::MutedByMe : State::Inactive);
setState(participant->mutedByMe
? State::MutedByMe
: State::Inactive);
setSounding(false);
setSpeaking(false);
} else {
setState(State::Muted);
setState(participant->raisedHandRating
? State::RaisedHand
: State::Muted);
setSounding(false);
setSpeaking(false);
}
refreshStatus();
}
void Row::setSpeaking(bool speaking) {
@ -406,7 +413,8 @@ void Row::setSpeaking(bool speaking) {
if (!_speaking
|| (_state == State::MutedByMe)
|| (_state == State::Muted)) {
|| (_state == State::Muted)
|| (_state == State::RaisedHand)) {
_statusIcon = nullptr;
} else if (!_statusIcon) {
_statusIcon = std::make_unique<StatusIcon>(
@ -456,7 +464,6 @@ void Row::setSounding(bool sounding) {
_blobsAnimation->lastTime = crl::now();
updateLevel(GroupCall::kSpeakLevelThreshold);
}
refreshStatus();
}
void Row::setState(State state) {
@ -464,10 +471,12 @@ void Row::setState(State state) {
return;
}
const auto wasActive = (_state == State::Active);
const auto wasMuted = (_state == State::Muted);
const auto wasMuted = (_state == State::Muted)
|| (_state == State::RaisedHand);
_state = state;
const auto nowActive = (_state == State::Active);
const auto nowMuted = (_state == State::Muted);
const auto nowMuted = (_state == State::Muted)
|| (_state == State::RaisedHand);
if (nowActive != wasActive) {
_activeAnimation.start(
[=] { _delegate->rowUpdateRow(this); },
@ -679,7 +688,9 @@ void Row::paintStatusText(
int outerWidth,
bool selected) {
const auto &font = st::normalFont;
const auto about = (_state == State::Inactive || _state == State::Muted)
const auto about = (_state == State::Inactive
|| _state == State::Muted
|| _state == State::RaisedHand)
? _aboutText
: QString();
if (_aboutText.isEmpty()
@ -752,12 +763,17 @@ void Row::paintAction(
}
}
const auto speaking = _speakingAnimation.value(_speaking ? 1. : 0.);
const auto active = _activeAnimation.value(
(_state == State::Active) ? 1. : 0.);
const auto active = _activeAnimation.value((_state == State::Active) ? 1. : 0.);
const auto muted = _mutedAnimation.value(
(_state == State::Muted) ? 1. : 0.);
(_state == State::Muted || _state == State::RaisedHand) ? 1. : 0.);
const auto mutedByMe = (_state == State::MutedByMe);
_delegate->rowPaintIcon(p, iconRect, speaking, active, muted, mutedByMe);
_delegate->rowPaintIcon(p, iconRect, {
.speaking = speaking,
.active = active,
.muted = muted,
.mutedByMe = (_state == State::MutedByMe),
.raisedHand = (_state == State::RaisedHand),
});
}
void Row::refreshStatus() {
@ -766,6 +782,8 @@ void Row::refreshStatus() {
? u"%1% %2"_q
.arg(std::round(_volume / 100.))
.arg(tr::lng_group_call_active(tr::now))
: (_state == State::RaisedHand)
? tr::lng_group_call_raised_hand_status(tr::now)
: tr::lng_group_call_inactive(tr::now)),
_speaking);
}
@ -1260,24 +1278,25 @@ void MembersController::rowUpdateRow(not_null<Row*> row) {
void MembersController::rowPaintIcon(
Painter &p,
QRect rect,
float64 speaking,
float64 active,
float64 muted,
bool mutedByMe) {
IconState state) {
const auto &greenIcon = st::groupCallMemberColoredCrossLine.icon;
const auto left = rect.x() + (rect.width() - greenIcon.width()) / 2;
const auto top = rect.y() + (rect.height() - greenIcon.height()) / 2;
if (speaking == 1. && !mutedByMe) {
if (state.speaking == 1. && !state.mutedByMe) {
// Just green icon, no cross, no coloring.
greenIcon.paintInCenter(p, rect);
return;
} else if (speaking == 0.) {
if (active == 1.) {
} else if (state.speaking == 0.) {
if (state.active == 1.) {
// Just gray icon, no cross, no coloring.
st::groupCallMemberInactiveCrossLine.icon.paintInCenter(p, rect);
return;
} else if (active == 0.) {
if (muted == 1.) {
} else if (state.active == 0.) {
if (state.muted == 1.) {
if (state.raisedHand) {
st::groupCallMemberRaisedHand.paintInCenter(p, rect);
return;
}
// Red crossed icon, colorized once, cached as last frame.
_coloredCrossLine.paint(
p,
@ -1286,7 +1305,7 @@ void MembersController::rowPaintIcon(
1.,
st::groupCallMemberMutedIcon->c);
return;
} else if (muted == 0.) {
} else if (state.muted == 0.) {
// Gray crossed icon, no coloring, cached as last frame.
_inactiveCrossLine.paint(p, left, top, 1.);
return;
@ -1295,17 +1314,18 @@ void MembersController::rowPaintIcon(
}
const auto activeInactiveColor = anim::color(
st::groupCallMemberInactiveIcon,
(mutedByMe
(state.mutedByMe
? st::groupCallMemberMutedIcon
: st::groupCallMemberActiveIcon),
speaking);
state.speaking);
const auto iconColor = anim::color(
activeInactiveColor,
st::groupCallMemberMutedIcon,
muted);
state.muted);
// Don't use caching of the last frame, because 'muted' may animate color.
const auto crossProgress = std::min(1. - active, 0.9999);
// Don't use caching of the last frame,
// because 'muted' may animate color.
const auto crossProgress = std::min(1. - state.active, 0.9999);
_inactiveCrossLine.paint(p, left, top, crossProgress, iconColor);
}
@ -1493,6 +1513,7 @@ void MembersController::addMuteActionsToContextMenu(
const auto muteState = row->state();
const auto isMuted = (muteState == Row::State::Muted)
|| (muteState == Row::State::RaisedHand)
|| (muteState == Row::State::MutedByMe);
auto mutesFromVolume = rpl::never<bool>() | rpl::type_erased();
@ -1553,7 +1574,7 @@ void MembersController::addMuteActionsToContextMenu(
const auto muteAction = [&]() -> QAction* {
if (muteState == Row::State::Invited
|| isMe(participantPeer)
|| (muteState == Row::State::Muted
|| (muteState == Row::State::Inactive
&& participantIsCallAdmin
&& _peer->canManageGroupCall())) {
return nullptr;
@ -1561,6 +1582,7 @@ void MembersController::addMuteActionsToContextMenu(
auto callback = [=] {
const auto state = row->state();
const auto muted = (state == Row::State::Muted)
|| (state == Row::State::RaisedHand)
|| (state == Row::State::MutedByMe);
toggleMute(!muted, false);
};

View File

@ -301,10 +301,13 @@ void GroupCall::applyParticipantsSlice(
: data.is_muted_by_you();
const auto onlyMinLoaded = data.is_min()
&& (!was || was->onlyMinLoaded);
const auto raisedHandRating
= data.vraise_hand_rating().value_or_empty();
const auto value = Participant{
.peer = participantPeer,
.date = data.vdate().v,
.lastActive = lastActive,
.raisedHandRating = raisedHandRating,
.ssrc = uint32(data.vsource().v),
.volume = volume,
.applyVolumeFromMin = applyVolumeFromMin,

View File

@ -24,6 +24,7 @@ struct GroupCallParticipant {
not_null<PeerData*> peer;
TimeId date = 0;
TimeId lastActive = 0;
uint64 raisedHandRating = 0;
uint32 ssrc = 0;
int volume = 0;
bool applyVolumeFromMin = true;