Start group call members list.
|
@ -266,6 +266,8 @@ PRIVATE
|
|||
calls/calls_call.h
|
||||
calls/calls_group_call.cpp
|
||||
calls/calls_group_call.h
|
||||
calls/calls_group_members.cpp
|
||||
calls/calls_group_members.h
|
||||
calls/calls_group_panel.cpp
|
||||
calls/calls_group_panel.h
|
||||
calls/calls_emoji_fingerprint.cpp
|
||||
|
|
Before Width: | Height: | Size: 595 B After Width: | Height: | Size: 595 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 209 B After Width: | Height: | Size: 209 B |
Before Width: | Height: | Size: 306 B After Width: | Height: | Size: 306 B |
Before Width: | Height: | Size: 449 B After Width: | Height: | Size: 449 B |
Before Width: | Height: | Size: 211 B After Width: | Height: | Size: 211 B |
Before Width: | Height: | Size: 298 B After Width: | Height: | Size: 298 B |
Before Width: | Height: | Size: 443 B After Width: | Height: | Size: 443 B |
Before Width: | Height: | Size: 377 B After Width: | Height: | Size: 377 B |
Before Width: | Height: | Size: 733 B After Width: | Height: | Size: 733 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 727 B After Width: | Height: | Size: 727 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 214 B After Width: | Height: | Size: 214 B |
Before Width: | Height: | Size: 426 B After Width: | Height: | Size: 426 B |
Before Width: | Height: | Size: 912 B After Width: | Height: | Size: 912 B |
Before Width: | Height: | Size: 460 B After Width: | Height: | Size: 460 B |
Before Width: | Height: | Size: 970 B After Width: | Height: | Size: 970 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 666 B After Width: | Height: | Size: 666 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 560 B After Width: | Height: | Size: 560 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 489 B After Width: | Height: | Size: 489 B |
Before Width: | Height: | Size: 1,002 B After Width: | Height: | Size: 1,002 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 744 B After Width: | Height: | Size: 744 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 100 B After Width: | Height: | Size: 100 B |
Before Width: | Height: | Size: 125 B After Width: | Height: | Size: 125 B |
Before Width: | Height: | Size: 139 B After Width: | Height: | Size: 139 B |
Before Width: | Height: | Size: 103 B After Width: | Height: | Size: 103 B |
Before Width: | Height: | Size: 127 B After Width: | Height: | Size: 127 B |
Before Width: | Height: | Size: 141 B After Width: | Height: | Size: 141 B |
Before Width: | Height: | Size: 295 B After Width: | Height: | Size: 295 B |
Before Width: | Height: | Size: 559 B After Width: | Height: | Size: 559 B |
Before Width: | Height: | Size: 927 B After Width: | Height: | Size: 927 B |
Before Width: | Height: | Size: 305 B After Width: | Height: | Size: 305 B |
Before Width: | Height: | Size: 524 B After Width: | Height: | Size: 524 B |
Before Width: | Height: | Size: 820 B After Width: | Height: | Size: 820 B |
Before Width: | Height: | Size: 385 B After Width: | Height: | Size: 385 B |
Before Width: | Height: | Size: 873 B After Width: | Height: | Size: 873 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 230 B After Width: | Height: | Size: 230 B |
Before Width: | Height: | Size: 393 B After Width: | Height: | Size: 393 B |
Before Width: | Height: | Size: 616 B After Width: | Height: | Size: 616 B |
Before Width: | Height: | Size: 333 B After Width: | Height: | Size: 333 B |
Before Width: | Height: | Size: 552 B After Width: | Height: | Size: 552 B |
Before Width: | Height: | Size: 889 B After Width: | Height: | Size: 889 B |
Before Width: | Height: | Size: 173 B After Width: | Height: | Size: 173 B |
Before Width: | Height: | Size: 304 B After Width: | Height: | Size: 304 B |
Before Width: | Height: | Size: 545 B After Width: | Height: | Size: 545 B |
Before Width: | Height: | Size: 228 B After Width: | Height: | Size: 228 B |
Before Width: | Height: | Size: 420 B After Width: | Height: | Size: 420 B |
Before Width: | Height: | Size: 661 B After Width: | Height: | Size: 661 B |
Before Width: | Height: | Size: 637 B After Width: | Height: | Size: 637 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 545 B After Width: | Height: | Size: 545 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 290 B After Width: | Height: | Size: 290 B |
Before Width: | Height: | Size: 499 B After Width: | Height: | Size: 499 B |
Before Width: | Height: | Size: 846 B After Width: | Height: | Size: 846 B |
Before Width: | Height: | Size: 420 B After Width: | Height: | Size: 420 B |
Before Width: | Height: | Size: 715 B After Width: | Height: | Size: 715 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
BIN
Telegram/Resources/icons/calls/group_calls_muted.png
Normal file
After Width: | Height: | Size: 718 B |
BIN
Telegram/Resources/icons/calls/group_calls_muted@2x.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/Resources/icons/calls/group_calls_muted@3x.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
Telegram/Resources/icons/calls/group_calls_unmuted.png
Normal file
After Width: | Height: | Size: 599 B |
BIN
Telegram/Resources/icons/calls/group_calls_unmuted@2x.png
Normal file
After Width: | Height: | Size: 1 KiB |
BIN
Telegram/Resources/icons/calls/group_calls_unmuted@3x.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
|
@ -219,7 +219,6 @@ contactsPhotoCheckIcon: defaultPeerListCheckIcon;
|
|||
contactsPhotoCheck: defaultPeerListCheck;
|
||||
contactsPhotoCheckbox: defaultPeerListCheckbox;
|
||||
contactsPhotoDisabledCheckFg: menuIconFg;
|
||||
contactsNameCheckedFg: windowActiveTextFg;
|
||||
contactsRipple: defaultRippleAnimation;
|
||||
|
||||
contactsMarginTop: 4px;
|
||||
|
|
|
@ -1283,7 +1283,7 @@ crl::time PeerListContent::paintRow(
|
|||
selected);
|
||||
}
|
||||
auto nameCheckedRatio = row->disabled() ? 0. : row->checkedRatio();
|
||||
p.setPen(anim::pen(st::contactsNameFg, st::contactsNameCheckedFg, nameCheckedRatio));
|
||||
p.setPen(anim::pen(_st.item.nameFg, _st.item.nameFgChecked, nameCheckedRatio));
|
||||
name.drawLeftElided(p, namex, _st.item.namePosition.y(), namew, width());
|
||||
|
||||
if (!actionSize.isEmpty()) {
|
||||
|
|
|
@ -135,7 +135,6 @@ private:
|
|||
// Viewing admins, banned or restricted users list with search.
|
||||
class ParticipantsBoxController
|
||||
: public PeerListController
|
||||
, private base::Subscriber
|
||||
, public base::has_weak_ptr {
|
||||
public:
|
||||
using Role = ParticipantsRole;
|
||||
|
|
|
@ -22,14 +22,14 @@ CallSignalBars {
|
|||
|
||||
callRadius: 6px;
|
||||
callShadow: Shadow {
|
||||
left: icon {{ "call_shadow_left", windowShadowFg }};
|
||||
topLeft: icon {{ "call_shadow_top_left", windowShadowFg }};
|
||||
top: icon {{ "call_shadow_top", windowShadowFg }};
|
||||
topRight: icon {{ "call_shadow_top_left-flip_horizontal", windowShadowFg }};
|
||||
right: icon {{ "call_shadow_left-flip_horizontal", windowShadowFg }};
|
||||
bottomRight: icon {{ "call_shadow_top_left-flip_vertical-flip_horizontal", windowShadowFg }};
|
||||
bottom: icon {{ "call_shadow_top-flip_vertical", windowShadowFg }};
|
||||
bottomLeft: icon {{ "call_shadow_top_left-flip_vertical", windowShadowFg }};
|
||||
left: icon {{ "calls/call_shadow_left", windowShadowFg }};
|
||||
topLeft: icon {{ "calls/call_shadow_top_left", windowShadowFg }};
|
||||
top: icon {{ "calls/call_shadow_top", windowShadowFg }};
|
||||
topRight: icon {{ "calls/call_shadow_top_left-flip_horizontal", windowShadowFg }};
|
||||
right: icon {{ "calls/call_shadow_left-flip_horizontal", windowShadowFg }};
|
||||
bottomRight: icon {{ "calls/call_shadow_top_left-flip_vertical-flip_horizontal", windowShadowFg }};
|
||||
bottom: icon {{ "calls/call_shadow_top-flip_vertical", windowShadowFg }};
|
||||
bottomLeft: icon {{ "calls/call_shadow_top_left-flip_vertical", windowShadowFg }};
|
||||
extend: margins(9px, 8px, 9px, 10px);
|
||||
fallback: windowShadowFgFallback;
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ callBodyWithPreview: CallBodyLayout {
|
|||
muteSize: 0px;
|
||||
mutePosition: point(90px, 84px);
|
||||
}
|
||||
callMutedPeerIcon: icon {{ "calls_mute_userpic", callIconFg }};
|
||||
callMutedPeerIcon: icon {{ "calls/calls_mute_userpic", callIconFg }};
|
||||
|
||||
callOutgoingPreviewMin: size(360px, 120px);
|
||||
callOutgoingPreview: size(540px, 180px); // default, for height == callHeight.
|
||||
|
@ -89,7 +89,7 @@ callSignalBarsPadding: margins(8px, 9px, 11px, 5px);
|
|||
callFingerprintTop: 8px;
|
||||
callFingerprintBottom: -16px;
|
||||
|
||||
callTooltipMutedIcon: icon{{ "calls_mute_tooltip", videoPlayIconFg }};
|
||||
callTooltipMutedIcon: icon{{ "calls/calls_mute_tooltip", videoPlayIconFg }};
|
||||
callTooltipMutedIconPosition: point(10px, 5px);
|
||||
callTooltipPadding: margins(41px, 7px, 15px, 8px);
|
||||
|
||||
|
@ -114,7 +114,7 @@ callButtonLabel: FlatLabel(defaultFlatLabel) {
|
|||
|
||||
callAnswer: CallButton {
|
||||
button: IconButton(callButton) {
|
||||
icon: icon {{ "call_answer", callIconFg }};
|
||||
icon: icon {{ "calls/call_answer", callIconFg }};
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: callAnswerRipple;
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ callAnswer: CallButton {
|
|||
}
|
||||
callHangup: CallButton {
|
||||
button: IconButton(callButton) {
|
||||
icon: icon {{ "call_discard", callIconFg }};
|
||||
icon: icon {{ "calls/call_discard", callIconFg }};
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: callHangupRipple;
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ callHangup: CallButton {
|
|||
}
|
||||
callCancel: CallButton {
|
||||
button: IconButton(callButton) {
|
||||
icon: icon {{ "call_cancel", callIconFgActive }};
|
||||
icon: icon {{ "calls/call_cancel", callIconFgActive }};
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: callIconActiveRipple;
|
||||
}
|
||||
|
@ -149,7 +149,7 @@ callCancel: CallButton {
|
|||
}
|
||||
callMicrophoneMute: CallButton {
|
||||
button: IconButton(callButton) {
|
||||
icon: icon {{ "call_record_active", callIconFg }};
|
||||
icon: icon {{ "calls/call_record_active", callIconFg }};
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: callMuteRipple;
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ callMicrophoneMute: CallButton {
|
|||
}
|
||||
callMicrophoneUnmute: CallButton(callMicrophoneMute) {
|
||||
button: IconButton(callButton) {
|
||||
icon: icon {{ "call_record_muted", callIconFgActive }};
|
||||
icon: icon {{ "calls/call_record_muted", callIconFgActive }};
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: callIconActiveRipple;
|
||||
}
|
||||
|
@ -169,7 +169,7 @@ callMicrophoneUnmute: CallButton(callMicrophoneMute) {
|
|||
}
|
||||
callCameraMute: CallButton(callMicrophoneMute) {
|
||||
button: IconButton(callButton) {
|
||||
icon: icon {{ "call_camera_active", callIconFg }};
|
||||
icon: icon {{ "calls/call_camera_active", callIconFg }};
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: callMuteRipple;
|
||||
}
|
||||
|
@ -177,7 +177,7 @@ callCameraMute: CallButton(callMicrophoneMute) {
|
|||
}
|
||||
callCameraUnmute: CallButton(callMicrophoneUnmute) {
|
||||
button: IconButton(callButton) {
|
||||
icon: icon {{ "call_camera_muted", callIconFgActive }};
|
||||
icon: icon {{ "calls/call_camera_muted", callIconFgActive }};
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: callIconActiveRipple;
|
||||
}
|
||||
|
@ -223,7 +223,7 @@ callBarMuteToggle: IconButton {
|
|||
width: 41px;
|
||||
height: 38px;
|
||||
|
||||
icon: icon {{ "call_record_active", callBarFg }};
|
||||
icon: icon {{ "calls/call_record_active", callBarFg }};
|
||||
iconPosition: point(3px, 2px);
|
||||
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
|
@ -232,11 +232,11 @@ callBarMuteToggle: IconButton {
|
|||
rippleAreaPosition: point(5px, 3px);
|
||||
rippleAreaSize: 32px;
|
||||
}
|
||||
callBarUnmuteIcon: icon {{ "call_record_muted", callBarFg }};
|
||||
callBarUnmuteIcon: icon {{ "calls/call_record_muted", callBarFg }};
|
||||
callBarRightSkip: 12px;
|
||||
callBarSkip: 10px;
|
||||
callBarHangup: IconButton(callBarMuteToggle) {
|
||||
icon: icon {{ "call_discard", callBarFg }};
|
||||
icon: icon {{ "calls/call_discard", callBarFg }};
|
||||
iconPosition: point(3px, 1px);
|
||||
}
|
||||
callBarLabel: LabelSimple(defaultLabelSimple) {
|
||||
|
@ -254,16 +254,16 @@ callBarInfoLabel: FlatLabel(defaultFlatLabel) {
|
|||
callBarLabelTop: 10px;
|
||||
|
||||
callArrowPosition: point(-2px, 1px);
|
||||
callArrowIn: icon {{ "call_arrow_in", callArrowFg }};
|
||||
callArrowOut: icon {{ "call_arrow_out", callArrowFg }};
|
||||
callArrowMissed: icon {{ "call_arrow_in", callArrowMissedFg }};
|
||||
callArrowIn: icon {{ "calls/call_arrow_in", callArrowFg }};
|
||||
callArrowOut: icon {{ "calls/call_arrow_out", callArrowFg }};
|
||||
callArrowMissed: icon {{ "calls/call_arrow_in", callArrowMissedFg }};
|
||||
callArrowSkip: 4px;
|
||||
callReDial: IconButton {
|
||||
width: 40px;
|
||||
height: 56px;
|
||||
|
||||
icon: icon {{ "call_answer", menuIconFg }};
|
||||
iconOver: icon {{ "call_answer", menuIconFgOver }};
|
||||
icon: icon {{ "calls/call_answer", menuIconFg }};
|
||||
iconOver: icon {{ "calls/call_answer", menuIconFgOver }};
|
||||
iconPosition: point(-1px, -1px);
|
||||
|
||||
ripple: defaultRippleAnimation;
|
||||
|
@ -272,8 +272,8 @@ callReDial: IconButton {
|
|||
}
|
||||
|
||||
callCameraReDial: IconButton(callReDial) {
|
||||
icon: icon {{ "call_camera_active", menuIconFg }};
|
||||
iconOver: icon {{ "call_camera_active", menuIconFgOver }};
|
||||
icon: icon {{ "calls/call_camera_active", menuIconFg }};
|
||||
iconOver: icon {{ "calls/call_camera_active", menuIconFgOver }};
|
||||
}
|
||||
|
||||
callRatingPadding: margins(24px, 12px, 24px, 0px);
|
||||
|
@ -281,7 +281,7 @@ callRatingStar: IconButton {
|
|||
width: 36px;
|
||||
height: 36px;
|
||||
|
||||
icon: icon {{ "call_rating", windowSubTextFg }};
|
||||
icon: icon {{ "calls/call_rating", windowSubTextFg }};
|
||||
iconPosition: point(-1px, -1px);
|
||||
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
|
@ -290,7 +290,7 @@ callRatingStar: IconButton {
|
|||
rippleAreaPosition: point(0px, 0px);
|
||||
rippleAreaSize: 36px;
|
||||
}
|
||||
callRatingStarFilled: icon {{ "call_rating_filled", lightButtonFg }};
|
||||
callRatingStarFilled: icon {{ "calls/call_rating_filled", lightButtonFg }};
|
||||
callRatingStarTop: 4px;
|
||||
callRatingComment: InputField(defaultInputField) {
|
||||
textMargins: margins(1px, 26px, 1px, 4px);
|
||||
|
@ -327,43 +327,43 @@ callTitleButton: IconButton {
|
|||
iconPosition: point(0px, 0px);
|
||||
}
|
||||
callTitleMinimizeIcon: icon {
|
||||
{ "calls_minimize_shadow", windowShadowFg },
|
||||
{ "calls_minimize_main", callNameFg },
|
||||
{ "calls/calls_minimize_shadow", windowShadowFg },
|
||||
{ "calls/calls_minimize_main", callNameFg },
|
||||
};
|
||||
callTitleMinimizeIconOver: icon {
|
||||
{ size(34px, 30px), callBgButton },
|
||||
{ size(34px, 30px), callMuteRipple },
|
||||
{ "calls_minimize_shadow", windowShadowFg },
|
||||
{ "calls_minimize_main", callNameFg },
|
||||
{ "calls/calls_minimize_shadow", windowShadowFg },
|
||||
{ "calls/calls_minimize_main", callNameFg },
|
||||
};
|
||||
callTitleMaximizeIcon: icon {
|
||||
{ "calls_maximize_shadow", windowShadowFg },
|
||||
{ "calls_maximize_main", callNameFg },
|
||||
{ "calls/calls_maximize_shadow", windowShadowFg },
|
||||
{ "calls/calls_maximize_main", callNameFg },
|
||||
};
|
||||
callTitleMaximizeIconOver: icon {
|
||||
{ size(34px, 30px), callBgButton },
|
||||
{ size(34px, 30px), callMuteRipple },
|
||||
{ "calls_maximize_shadow", windowShadowFg },
|
||||
{ "calls_maximize_main", callNameFg },
|
||||
{ "calls/calls_maximize_shadow", windowShadowFg },
|
||||
{ "calls/calls_maximize_main", callNameFg },
|
||||
};
|
||||
callTitleRestoreIcon: icon {
|
||||
{ "calls_restore_shadow", windowShadowFg },
|
||||
{ "calls_restore_main", callNameFg },
|
||||
{ "calls/calls_restore_shadow", windowShadowFg },
|
||||
{ "calls/calls_restore_main", callNameFg },
|
||||
};
|
||||
callTitleRestoreIconOver: icon {
|
||||
{ size(34px, 30px), callBgButton },
|
||||
{ size(34px, 30px), callMuteRipple },
|
||||
{ "calls_restore_shadow", windowShadowFg },
|
||||
{ "calls_restore_main", callNameFg },
|
||||
{ "calls/calls_restore_shadow", windowShadowFg },
|
||||
{ "calls/calls_restore_main", callNameFg },
|
||||
};
|
||||
callTitleCloseIcon: icon {
|
||||
{ "calls_close_shadow", windowShadowFg },
|
||||
{ "calls_close_main", callNameFg },
|
||||
{ "calls/calls_close_shadow", windowShadowFg },
|
||||
{ "calls/calls_close_main", callNameFg },
|
||||
};
|
||||
callTitleCloseIconOver: icon {
|
||||
{ size(34px, 30px), titleButtonCloseBgOver },
|
||||
{ "calls_close_shadow", windowShadowFg },
|
||||
{ "calls_close_main", titleButtonCloseFgOver },
|
||||
{ "calls/calls_close_shadow", windowShadowFg },
|
||||
{ "calls/calls_close_main", titleButtonCloseFgOver },
|
||||
};
|
||||
callTitle: WindowTitle(defaultWindowTitle) {
|
||||
height: 0px;
|
||||
|
@ -394,7 +394,7 @@ callTitle: WindowTitle(defaultWindowTitle) {
|
|||
closeIconActive: callTitleCloseIcon;
|
||||
closeIconActiveOver: callTitleCloseIconOver;
|
||||
}
|
||||
callTitleShadow: icon {{ "calls_shadow_controls", windowShadowFg }};
|
||||
callTitleShadow: icon {{ "calls/calls_shadow_controls", windowShadowFg }};
|
||||
|
||||
callErrorToast: Toast(defaultToast) {
|
||||
minWidth: 240px;
|
||||
|
@ -402,3 +402,78 @@ callErrorToast: Toast(defaultToast) {
|
|||
|
||||
groupCallWidth: 380px;
|
||||
groupCallHeight: 580px;
|
||||
|
||||
groupCallRipple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: groupCallMembersRipple;
|
||||
}
|
||||
groupCallMembersList: PeerList(defaultPeerList) {
|
||||
item: PeerListItem(defaultPeerListItem) {
|
||||
button: OutlineButton(defaultPeerListButton) {
|
||||
textBg: groupCallMembersBg;
|
||||
textBgOver: groupCallMembersBg;
|
||||
|
||||
textFg: groupCallMemberInactiveStatus;
|
||||
textFgOver: groupCallMemberInactiveStatus;
|
||||
|
||||
font: normalFont;
|
||||
padding: margins(11px, 5px, 11px, 5px);
|
||||
|
||||
ripple: groupCallRipple;
|
||||
}
|
||||
height: 52px;
|
||||
photoPosition: point(12px, 6px);
|
||||
namePosition: point(68px, 7px);
|
||||
statusPosition: point(68px, 27px);
|
||||
photoSize: 40px;
|
||||
nameFg: groupCallMembersFg;
|
||||
nameFgChecked: groupCallMembersFg;
|
||||
statusFg: groupCallMemberInactiveStatus;
|
||||
statusFgOver: groupCallMemberInactiveStatus;
|
||||
statusFgActive: groupCallMemberActiveStatus;
|
||||
}
|
||||
}
|
||||
groupCallMembersHeader: 47px;
|
||||
groupCallMembersMargin: margins(16px, 16px, 16px, 28px);
|
||||
groupCallAddMember: IconButton(defaultIconButton) {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
iconPosition: point(3px, 5px);
|
||||
icon: icon {{ "info_add_member", groupCallMemberInactiveIcon }};
|
||||
iconOver: icon {{ "info_add_member", groupCallMemberInactiveIcon }};
|
||||
rippleAreaPosition: point(0px, 0px);
|
||||
rippleAreaSize: 36px;
|
||||
ripple: groupCallRipple;
|
||||
}
|
||||
groupCallHeaderPosition: point(16px, 16px);
|
||||
groupCallHeaderLabel: FlatLabel(defaultFlatLabel) {
|
||||
maxHeight: 18px;
|
||||
textFg: groupCallMembersFg;
|
||||
style: TextStyle(defaultTextStyle) {
|
||||
font: semiboldFont;
|
||||
linkFont: semiboldFont;
|
||||
linkFontOver: semiboldFont;
|
||||
}
|
||||
}
|
||||
groupCallAddButtonPosition: point(10px, 7px);
|
||||
|
||||
groupCallInactiveButton: IconButton {
|
||||
width: 36px;
|
||||
height: 52px;
|
||||
|
||||
icon: icon {{ "calls/group_calls_muted", groupCallMemberInactiveIcon }};
|
||||
iconOver: icon {{ "calls/group_calls_muted", groupCallMemberInactiveIcon }};
|
||||
iconPosition: point(-1px, -1px);
|
||||
|
||||
ripple: groupCallRipple;
|
||||
rippleAreaPosition: point(0px, 8px);
|
||||
rippleAreaSize: 36px;
|
||||
}
|
||||
groupCallActiveButton: IconButton(groupCallInactiveButton) {
|
||||
icon: icon {{ "calls/group_calls_unmuted", groupCallMemberActiveIcon }};
|
||||
iconOver: icon {{ "calls/group_calls_unmuted", groupCallMemberActiveIcon }};
|
||||
}
|
||||
groupCallMutedButton: IconButton(groupCallInactiveButton) {
|
||||
icon: icon {{ "calls/group_calls_muted", groupCallMemberMutedIcon }};
|
||||
iconOver: icon {{ "calls/group_calls_muted", groupCallMemberMutedIcon }};
|
||||
}
|
||||
groupCallMemberButtonSkip: 10px;
|
||||
|
|
528
Telegram/SourceFiles/calls/calls_group_members.cpp
Normal file
|
@ -0,0 +1,528 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "calls/calls_group_members.h"
|
||||
|
||||
#include "calls/calls_group_call.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_group_call.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
#include "main/main_session.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "styles/style_calls.h"
|
||||
|
||||
namespace Calls {
|
||||
namespace {
|
||||
|
||||
class MembersController final
|
||||
: public PeerListController
|
||||
, public base::has_weak_ptr {
|
||||
public:
|
||||
explicit MembersController(not_null<GroupCall*> call);
|
||||
|
||||
Main::Session &session() const override;
|
||||
void prepare() override;
|
||||
void rowClicked(not_null<PeerListRow*> row) override;
|
||||
void rowActionClicked(not_null<PeerListRow*> row) override;
|
||||
base::unique_qptr<Ui::PopupMenu> rowContextMenu(
|
||||
QWidget *parent,
|
||||
not_null<PeerListRow*> row) override;
|
||||
void loadMoreRows() override;
|
||||
|
||||
private:
|
||||
[[nodiscard]] std::unique_ptr<PeerListRow> createRow(
|
||||
not_null<UserData*> user) const;
|
||||
|
||||
void prepareRows();
|
||||
|
||||
void setupListChangeViewers();
|
||||
bool appendRow(not_null<UserData*> user);
|
||||
bool prependRow(not_null<UserData*> user);
|
||||
bool removeRow(not_null<UserData*> user);
|
||||
|
||||
const base::weak_ptr<GroupCall> _call;
|
||||
const not_null<ChannelData*> _channel;
|
||||
|
||||
Ui::BoxPointer _addBox;
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
||||
class Row final : public PeerListRow {
|
||||
public:
|
||||
Row(not_null<ChannelData*> channel, not_null<UserData*> user);
|
||||
|
||||
enum class State {
|
||||
Active,
|
||||
Inactive,
|
||||
Muted,
|
||||
};
|
||||
|
||||
void addActionRipple(QPoint point, Fn<void()> updateCallback) override;
|
||||
void stopLastActionRipple() override;
|
||||
|
||||
int nameIconWidth() const override {
|
||||
return 0;
|
||||
}
|
||||
QSize actionSize() const override {
|
||||
return QSize(_st->width, _st->height);
|
||||
}
|
||||
QMargins actionMargins() const override {
|
||||
return QMargins(
|
||||
0,
|
||||
0,
|
||||
st::groupCallMemberButtonSkip,
|
||||
0);
|
||||
}
|
||||
void paintAction(
|
||||
Painter &p,
|
||||
int x,
|
||||
int y,
|
||||
int outerWidth,
|
||||
bool selected,
|
||||
bool actionSelected) override;
|
||||
|
||||
private:
|
||||
void refreshStatus() override;
|
||||
|
||||
[[nodiscard]] static State ComputeState(
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<UserData*> user);
|
||||
[[nodiscard]] static not_null<const style::IconButton*> ComputeIconStyle(
|
||||
State state);
|
||||
|
||||
State _state = State::Inactive;
|
||||
not_null<const style::IconButton*> _st;
|
||||
|
||||
std::unique_ptr<Ui::RippleAnimation> _actionRipple;
|
||||
|
||||
};
|
||||
|
||||
Row::Row(not_null<ChannelData*> channel, not_null<UserData*> user)
|
||||
: PeerListRow(user)
|
||||
, _state(ComputeState(channel, user))
|
||||
, _st(ComputeIconStyle(_state)) {
|
||||
refreshStatus();
|
||||
}
|
||||
|
||||
void Row::paintAction(
|
||||
Painter &p,
|
||||
int x,
|
||||
int y,
|
||||
int outerWidth,
|
||||
bool selected,
|
||||
bool actionSelected) {
|
||||
auto size = actionSize();
|
||||
if (_actionRipple) {
|
||||
_actionRipple->paint(
|
||||
p,
|
||||
x + _st->rippleAreaPosition.x(),
|
||||
y + _st->rippleAreaPosition.y(),
|
||||
outerWidth);
|
||||
if (_actionRipple->empty()) {
|
||||
_actionRipple.reset();
|
||||
}
|
||||
}
|
||||
_st->icon.paintInCenter(
|
||||
p,
|
||||
style::rtlrect(x, y, size.width(), size.height(), outerWidth));
|
||||
}
|
||||
|
||||
void Row::refreshStatus() {
|
||||
setCustomStatus([&] {
|
||||
switch (_state) {
|
||||
case State::Inactive:
|
||||
case State::Muted: return "listening";
|
||||
case State::Active: return "speaking";
|
||||
}
|
||||
return "";
|
||||
}());
|
||||
}
|
||||
|
||||
Row::State Row::ComputeState(
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<UserData*> user) {
|
||||
const auto call = channel->call();
|
||||
if (!call) {
|
||||
return State::Inactive;
|
||||
}
|
||||
const auto &participants = call->participants();
|
||||
const auto i = ranges::find(
|
||||
participants,
|
||||
user,
|
||||
&Data::GroupCall::Participant::user);
|
||||
if (i == end(participants)) {
|
||||
return State::Inactive;
|
||||
}
|
||||
return !i->muted
|
||||
? State::Active
|
||||
: i->canSelfUnmute
|
||||
? State::Inactive
|
||||
: State::Muted;
|
||||
}
|
||||
|
||||
not_null<const style::IconButton*> Row::ComputeIconStyle(
|
||||
State state) {
|
||||
switch (state) {
|
||||
case State::Inactive: return &st::groupCallInactiveButton;
|
||||
case State::Active: return &st::groupCallActiveButton;
|
||||
case State::Muted: return &st::groupCallMutedButton;
|
||||
}
|
||||
Unexpected("State in Row::ComputeIconStyle.");
|
||||
}
|
||||
|
||||
void Row::addActionRipple(QPoint point, Fn<void()> updateCallback) {
|
||||
if (!_actionRipple) {
|
||||
auto mask = Ui::RippleAnimation::ellipseMask(
|
||||
QSize(_st->rippleAreaSize, _st->rippleAreaSize));
|
||||
_actionRipple = std::make_unique<Ui::RippleAnimation>(
|
||||
_st->ripple,
|
||||
std::move(mask),
|
||||
std::move(updateCallback));
|
||||
}
|
||||
_actionRipple->add(point - _st->rippleAreaPosition);
|
||||
}
|
||||
|
||||
void Row::stopLastActionRipple() {
|
||||
if (_actionRipple) {
|
||||
_actionRipple->lastStop();
|
||||
}
|
||||
}
|
||||
|
||||
MembersController::MembersController(not_null<GroupCall*> call)
|
||||
: _call(call)
|
||||
, _channel(call->channel()) {
|
||||
setupListChangeViewers();
|
||||
}
|
||||
|
||||
void MembersController::setupListChangeViewers() {
|
||||
const auto call = _call.get();
|
||||
const auto channel = call->channel();
|
||||
channel->session().changes().peerUpdates(
|
||||
channel,
|
||||
Data::PeerUpdate::Flag::GroupCall
|
||||
) | rpl::start_with_next([=] {
|
||||
prepareRows();
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
Main::Session &MembersController::session() const {
|
||||
return _call->channel()->session();
|
||||
}
|
||||
|
||||
void MembersController::prepare() {
|
||||
delegate()->peerListSetSearchMode(PeerListSearchMode::Disabled);
|
||||
//delegate()->peerListSetTitle(std::move(title));
|
||||
setDescriptionText(tr::lng_contacts_loading(tr::now));
|
||||
setSearchNoResultsText(tr::lng_blocked_list_not_found(tr::now));
|
||||
|
||||
prepareRows();
|
||||
delegate()->peerListRefreshRows();
|
||||
|
||||
loadMoreRows();
|
||||
}
|
||||
|
||||
void MembersController::prepareRows() {
|
||||
const auto real = _channel->call();
|
||||
if (!real) {
|
||||
return;
|
||||
}
|
||||
auto foundSelf = false;
|
||||
auto changed = false;
|
||||
const auto &participants = real->participants();
|
||||
auto count = delegate()->peerListFullRowsCount();
|
||||
for (auto i = 0; i != count;) {
|
||||
auto row = delegate()->peerListRowAt(i);
|
||||
auto user = row->peer()->asUser();
|
||||
if (user->isSelf()) {
|
||||
foundSelf = true;
|
||||
}
|
||||
const auto contains = ranges::contains(
|
||||
participants,
|
||||
not_null{ user },
|
||||
&Data::GroupCall::Participant::user);
|
||||
if (contains) {
|
||||
++i;
|
||||
} else {
|
||||
changed = true;
|
||||
delegate()->peerListRemoveRow(row);
|
||||
--count;
|
||||
}
|
||||
}
|
||||
if (!foundSelf) {
|
||||
if (auto row = createRow(_channel->session().user())) {
|
||||
changed = true;
|
||||
delegate()->peerListAppendRow(std::move(row));
|
||||
}
|
||||
}
|
||||
for (const auto &participant : participants) {
|
||||
if (auto row = createRow(participant.user)) {
|
||||
changed = true;
|
||||
delegate()->peerListAppendRow(std::move(row));
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
delegate()->peerListRefreshRows();
|
||||
}
|
||||
}
|
||||
|
||||
void MembersController::loadMoreRows() {
|
||||
if (const auto call = _call.get()) {
|
||||
if (const auto real = call->channel()->call()) {
|
||||
real->requestParticipants();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MembersController::rowClicked(not_null<PeerListRow*> row) {
|
||||
Expects(row->peer()->isUser());
|
||||
|
||||
const auto user = row->peer()->asUser();
|
||||
}
|
||||
|
||||
void MembersController::rowActionClicked(
|
||||
not_null<PeerListRow*> row) {
|
||||
Expects(row->peer()->isUser());
|
||||
|
||||
const auto user = row->peer()->asUser();
|
||||
}
|
||||
|
||||
base::unique_qptr<Ui::PopupMenu> MembersController::rowContextMenu(
|
||||
QWidget *parent,
|
||||
not_null<PeerListRow*> row) {
|
||||
Expects(row->peer()->isUser());
|
||||
|
||||
const auto user = row->peer()->asUser();
|
||||
auto result = base::make_unique_q<Ui::PopupMenu>(parent);
|
||||
//result->addAction( // #TODO calls
|
||||
// tr::lng_context_view_profile(tr::now),
|
||||
// crl::guard(this, [=] { _navigation->showPeerInfo(user); }));
|
||||
return result;
|
||||
}
|
||||
|
||||
bool MembersController::appendRow(not_null<UserData*> user) {
|
||||
if (delegate()->peerListFindRow(user->id)) {
|
||||
return false;
|
||||
}
|
||||
delegate()->peerListAppendRow(createRow(user));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MembersController::prependRow(not_null<UserData*> user) {
|
||||
if (auto row = delegate()->peerListFindRow(user->id)) {
|
||||
return false;
|
||||
}
|
||||
delegate()->peerListPrependRow(createRow(user));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MembersController::removeRow(not_null<UserData*> user) {
|
||||
if (auto row = delegate()->peerListFindRow(user->id)) {
|
||||
delegate()->peerListRemoveRow(row);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<PeerListRow> MembersController::createRow(
|
||||
not_null<UserData*> user) const {
|
||||
return std::make_unique<Row>(_channel, user);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
GroupMembers::GroupMembers(
|
||||
QWidget *parent,
|
||||
not_null<GroupCall*> call)
|
||||
: RpWidget(parent)
|
||||
, _call(call)
|
||||
, _scroll(this, st::defaultSolidScroll)
|
||||
, _listController(std::make_unique<MembersController>(call)) {
|
||||
setupHeader(call);
|
||||
setupList();
|
||||
setContent(_list);
|
||||
_listController->setDelegate(static_cast<PeerListDelegate*>(this));
|
||||
|
||||
paintRequest(
|
||||
) | rpl::start_with_next([=](QRect clip) {
|
||||
QPainter(this).fillRect(clip, st::groupCallMembersBg);
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
int GroupMembers::desiredHeight() const {
|
||||
auto desired = _header ? _header->height() : 0;
|
||||
auto count = [this] {
|
||||
if (const auto call = _call.get()) {
|
||||
if (const auto real = call->channel()->call()) {
|
||||
return real->fullCount();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}();
|
||||
desired += qMax(count, _list->fullRowsCount())
|
||||
* st::groupCallMembersList.item.height;
|
||||
return qMax(height(), desired);
|
||||
}
|
||||
|
||||
void GroupMembers::setupHeader(not_null<GroupCall*> call) {
|
||||
_header = object_ptr<Ui::FixedHeightWidget>(
|
||||
this,
|
||||
st::groupCallMembersHeader);
|
||||
auto parent = _header.data();
|
||||
|
||||
_titleWrap = Ui::CreateChild<Ui::RpWidget>(parent);
|
||||
_title = setupTitle(call);
|
||||
_addMember = Ui::CreateChild<Ui::IconButton>(
|
||||
parent,
|
||||
st::groupCallAddMember);
|
||||
setupButtons();
|
||||
|
||||
widthValue(
|
||||
) | rpl::start_with_next([this](int width) {
|
||||
_header->resizeToWidth(width);
|
||||
}, _header->lifetime());
|
||||
}
|
||||
|
||||
object_ptr<Ui::FlatLabel> GroupMembers::setupTitle(
|
||||
not_null<GroupCall*> call) {
|
||||
const auto channel = call->channel();
|
||||
auto count = channel->session().changes().peerFlagsValue(
|
||||
channel,
|
||||
Data::PeerUpdate::Flag::GroupCall
|
||||
) | rpl::map([=] {
|
||||
const auto call = channel->call();
|
||||
return std::max(call ? call->fullCount() : 0, 1);
|
||||
});
|
||||
auto result = object_ptr<Ui::FlatLabel>(
|
||||
_titleWrap,
|
||||
tr::lng_chat_status_members(
|
||||
lt_count_decimal,
|
||||
std::move(count) | tr::to_count(),
|
||||
Ui::Text::Upper
|
||||
),
|
||||
st::groupCallHeaderLabel);
|
||||
result->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
return result;
|
||||
}
|
||||
|
||||
void GroupMembers::setupButtons() {
|
||||
using namespace rpl::mappers;
|
||||
|
||||
_addMember->showOn(rpl::single(true));
|
||||
_addMember->addClickHandler([=] { // TODO throttle(ripple duration)
|
||||
addMember();
|
||||
});
|
||||
}
|
||||
|
||||
void GroupMembers::setupList() {
|
||||
auto topSkip = _header ? _header->height() : 0;
|
||||
_list = _scroll->setOwnedWidget(object_ptr<ListWidget>(
|
||||
this,
|
||||
_listController.get(),
|
||||
st::groupCallMembersList));
|
||||
|
||||
sizeValue(
|
||||
) | rpl::start_with_next([=](QSize size) {
|
||||
_scroll->setGeometry(0, topSkip, size.width(), size.height() - topSkip);
|
||||
_list->resizeToWidth(size.width());
|
||||
}, _list->lifetime());
|
||||
|
||||
_list->heightValue(
|
||||
) | rpl::start_with_next([=](int listHeight) {
|
||||
auto newHeight = (listHeight > 0)
|
||||
? (topSkip + listHeight)
|
||||
: 0;
|
||||
resize(width(), newHeight);
|
||||
}, _list->lifetime());
|
||||
_list->moveToLeft(0, topSkip);
|
||||
_list->show();
|
||||
}
|
||||
|
||||
void GroupMembers::resizeEvent(QResizeEvent *e) {
|
||||
if (_header) {
|
||||
updateHeaderControlsGeometry(width());
|
||||
}
|
||||
}
|
||||
|
||||
void GroupMembers::updateHeaderControlsGeometry(int newWidth) {
|
||||
auto availableWidth = newWidth
|
||||
- st::groupCallAddButtonPosition.x();
|
||||
_addMember->moveToLeft(
|
||||
availableWidth - _addMember->width(),
|
||||
st::groupCallAddButtonPosition.y(),
|
||||
newWidth);
|
||||
if (!_addMember->isHidden()) {
|
||||
availableWidth -= _addMember->width();
|
||||
}
|
||||
|
||||
_titleWrap->resize(
|
||||
availableWidth - _addMember->width() - st::groupCallHeaderPosition.x(),
|
||||
_title->height());
|
||||
_titleWrap->moveToLeft(
|
||||
st::groupCallHeaderPosition.x(),
|
||||
st::groupCallHeaderPosition.y(),
|
||||
newWidth);
|
||||
_titleWrap->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
_title->resizeToWidth(_titleWrap->width());
|
||||
_title->moveToLeft(0, 0);
|
||||
}
|
||||
|
||||
void GroupMembers::addMember() {
|
||||
// #TODO calls
|
||||
}
|
||||
|
||||
void GroupMembers::visibleTopBottomUpdated(
|
||||
int visibleTop,
|
||||
int visibleBottom) {
|
||||
setChildVisibleTopBottom(_list, visibleTop, visibleBottom);
|
||||
}
|
||||
|
||||
void GroupMembers::peerListSetTitle(rpl::producer<QString> title) {
|
||||
}
|
||||
|
||||
void GroupMembers::peerListSetAdditionalTitle(rpl::producer<QString> title) {
|
||||
}
|
||||
|
||||
bool GroupMembers::peerListIsRowChecked(not_null<PeerListRow*> row) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void GroupMembers::peerListScrollToTop() {
|
||||
}
|
||||
|
||||
int GroupMembers::peerListSelectedRowsCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<not_null<PeerData*>> GroupMembers::peerListCollectSelectedRows() {
|
||||
return {};
|
||||
}
|
||||
|
||||
void GroupMembers::peerListAddSelectedPeerInBunch(not_null<PeerData*> peer) {
|
||||
Unexpected("Item selection in Info::Profile::Members.");
|
||||
}
|
||||
|
||||
void GroupMembers::peerListAddSelectedRowInBunch(not_null<PeerListRow*> row) {
|
||||
Unexpected("Item selection in Info::Profile::Members.");
|
||||
}
|
||||
|
||||
void GroupMembers::peerListFinishSelectedRowsBunch() {
|
||||
}
|
||||
|
||||
void GroupMembers::peerListSetDescription(
|
||||
object_ptr<Ui::FlatLabel> description) {
|
||||
description.destroy();
|
||||
}
|
||||
|
||||
} // namespace Calls
|
76
Telegram/SourceFiles/calls/calls_group_members.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "boxes/peer_list_box.h"
|
||||
|
||||
namespace Ui {
|
||||
class ScrollArea;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Calls {
|
||||
|
||||
class GroupCall;
|
||||
|
||||
class GroupMembers final
|
||||
: public Ui::RpWidget
|
||||
, private PeerListContentDelegate {
|
||||
public:
|
||||
GroupMembers(
|
||||
QWidget *parent,
|
||||
not_null<GroupCall*> call);
|
||||
|
||||
int desiredHeight() const;
|
||||
|
||||
private:
|
||||
using ListWidget = PeerListContent;
|
||||
|
||||
void visibleTopBottomUpdated(
|
||||
int visibleTop,
|
||||
int visibleBottom) override;
|
||||
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
||||
// PeerListContentDelegate interface.
|
||||
void peerListSetTitle(rpl::producer<QString> title) override;
|
||||
void peerListSetAdditionalTitle(rpl::producer<QString> title) override;
|
||||
bool peerListIsRowChecked(not_null<PeerListRow*> row) override;
|
||||
int peerListSelectedRowsCount() override;
|
||||
void peerListScrollToTop() override;
|
||||
std::vector<not_null<PeerData*>> peerListCollectSelectedRows() override;
|
||||
void peerListAddSelectedPeerInBunch(
|
||||
not_null<PeerData*> peer) override;
|
||||
void peerListAddSelectedRowInBunch(
|
||||
not_null<PeerListRow*> row) override;
|
||||
void peerListFinishSelectedRowsBunch() override;
|
||||
void peerListSetDescription(
|
||||
object_ptr<Ui::FlatLabel> description) override;
|
||||
|
||||
void setupHeader(not_null<GroupCall*> call);
|
||||
object_ptr<Ui::FlatLabel> setupTitle(not_null<GroupCall*> call);
|
||||
void setupList();
|
||||
|
||||
void setupButtons();
|
||||
|
||||
void addMember();
|
||||
void showMembersWithSearch(bool withSearch);
|
||||
void updateHeaderControlsGeometry(int newWidth);
|
||||
|
||||
base::weak_ptr<GroupCall> _call;
|
||||
object_ptr<Ui::ScrollArea> _scroll;
|
||||
std::unique_ptr<PeerListController> _listController;
|
||||
object_ptr<Ui::RpWidget> _header = { nullptr };
|
||||
ListWidget *_list = { nullptr };
|
||||
|
||||
Ui::RpWidget *_titleWrap = nullptr;
|
||||
Ui::FlatLabel *_title = nullptr;
|
||||
Ui::IconButton *_addMember = nullptr;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Calls
|
|
@ -7,42 +7,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "calls/calls_group_panel.h"
|
||||
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_photo_media.h"
|
||||
#include "data/data_cloud_file.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "calls/calls_emoji_fingerprint.h"
|
||||
#include "calls/calls_signal_bars.h"
|
||||
#include "calls/calls_userpic.h"
|
||||
#include "calls/calls_video_bubble.h"
|
||||
#include "calls/calls_group_members.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "ui/widgets/window.h"
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/text/format_values.h"
|
||||
#include "ui/wrap/fade_wrap.h"
|
||||
#include "ui/wrap/padding_wrap.h"
|
||||
#include "ui/platform/ui_platform_utility.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/empty_userpic.h"
|
||||
#include "ui/emoji_config.h"
|
||||
#include "core/application.h"
|
||||
#include "mainwindow.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "platform/platform_specific.h"
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "window/main_window.h"
|
||||
#include "base/event_filter.h"
|
||||
#include "app.h"
|
||||
#include "webrtc/webrtc_video_track.h"
|
||||
#include "styles/style_calls.h"
|
||||
#include "styles/style_chat.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include "ui/platform/win/ui_window_title_win.h"
|
||||
|
@ -246,9 +219,9 @@ GroupPanel::GroupPanel(not_null<GroupCall*> call)
|
|||
#ifdef Q_OS_WIN
|
||||
, _controls(std::make_unique<Ui::Platform::TitleControls>(
|
||||
_window.get(),
|
||||
st::callTitle,
|
||||
[=](bool maximized) { toggleFullScreen(maximized); }))
|
||||
st::callTitle))
|
||||
#endif // Q_OS_WIN
|
||||
, _members(widget(), call)
|
||||
, _settings(widget(), st::callCancel)
|
||||
, _hangup(widget(), st::callHangup)
|
||||
, _mute(widget(), st::callMicrophoneMute, &st::callMicrophoneUnmute) {
|
||||
|
@ -262,6 +235,9 @@ GroupPanel::GroupPanel(not_null<GroupCall*> call)
|
|||
GroupPanel::~GroupPanel() = default;
|
||||
|
||||
void GroupPanel::showAndActivate() {
|
||||
if (_window->isHidden()) {
|
||||
_window->show();
|
||||
}
|
||||
_window->raise();
|
||||
_window->setWindowState(_window->windowState() | Qt::WindowActive);
|
||||
_window->activateWindow();
|
||||
|
@ -276,17 +252,13 @@ void GroupPanel::initWindow() {
|
|||
_window->setTitle(u" "_q);
|
||||
_window->setTitleStyle(st::callTitle);
|
||||
|
||||
_window->events(
|
||||
) | rpl::start_with_next([=](not_null<QEvent*> e) {
|
||||
if (e->type() == QEvent::Close) {
|
||||
handleClose();
|
||||
} else if (e->type() == QEvent::KeyPress) {
|
||||
if ((static_cast<QKeyEvent*>(e.get())->key() == Qt::Key_Escape)
|
||||
&& _window->isFullScreen()) {
|
||||
_window->showNormal();
|
||||
}
|
||||
base::install_event_filter(_window.get(), [=](not_null<QEvent*> e) {
|
||||
if (e->type() == QEvent::Close && handleClose()) {
|
||||
e->ignore();
|
||||
return base::EventFilterResult::Cancel;
|
||||
}
|
||||
}, _window->lifetime());
|
||||
return base::EventFilterResult::Continue;
|
||||
});
|
||||
|
||||
_window->setBodyTitleArea([=](QPoint widgetPoint) {
|
||||
using Flag = Ui::WindowTitleHitTestFlag;
|
||||
|
@ -301,31 +273,8 @@ void GroupPanel::initWindow() {
|
|||
const auto inControls = false;
|
||||
return inControls
|
||||
? Flag::None
|
||||
: (Flag::Move | Flag::FullScreen);
|
||||
: (Flag::Move | Flag::Maximize);
|
||||
});
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
// On Windows we replace snap-to-top maximizing with fullscreen.
|
||||
//
|
||||
// We have to switch first to showNormal, so that showFullScreen
|
||||
// will remember correct normal window geometry and next showNormal
|
||||
// will show it instead of a moving maximized window.
|
||||
//
|
||||
// We have to do it in InvokeQueued, otherwise it still captures
|
||||
// the maximized window geometry and saves it.
|
||||
//
|
||||
// I couldn't find a less glitchy way to do that *sigh*.
|
||||
const auto object = _window->windowHandle();
|
||||
const auto signal = &QWindow::windowStateChanged;
|
||||
QObject::connect(object, signal, [=](Qt::WindowState state) {
|
||||
if (state == Qt::WindowMaximized) {
|
||||
InvokeQueued(object, [=] {
|
||||
_window->showNormal();
|
||||
_window->showFullScreen();
|
||||
});
|
||||
}
|
||||
});
|
||||
#endif // Q_OS_WIN
|
||||
}
|
||||
|
||||
void GroupPanel::initWidget() {
|
||||
|
@ -382,6 +331,11 @@ void GroupPanel::initWithCall(GroupCall *call) {
|
|||
) | rpl::start_with_next([=](State state) {
|
||||
stateChanged(state);
|
||||
}, _callLifetime);
|
||||
|
||||
_members->desiredHeightValue(
|
||||
) | rpl::start_with_next([=] {
|
||||
updateControlsGeometry();
|
||||
}, _members->lifetime());
|
||||
}
|
||||
|
||||
void GroupPanel::initLayout() {
|
||||
|
@ -412,22 +366,33 @@ void GroupPanel::initGeometry() {
|
|||
updateControlsGeometry();
|
||||
}
|
||||
|
||||
void GroupPanel::toggleFullScreen(bool fullscreen) {
|
||||
if (fullscreen) {
|
||||
_window->showFullScreen();
|
||||
} else {
|
||||
_window->showNormal();
|
||||
}
|
||||
}
|
||||
|
||||
void GroupPanel::updateControlsGeometry() {
|
||||
if (widget()->size().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
const auto top = widget()->height() - 2 * _mute->height();
|
||||
_mute->move((widget()->width() - _mute->width()) / 2, top);
|
||||
_settings->moveToLeft(_settings->width(), top);
|
||||
_hangup->moveToRight(_settings->width(), top);
|
||||
const auto desiredHeight = _members->desiredHeight();
|
||||
const auto membersWidth = widget()->width()
|
||||
- st::groupCallMembersMargin.left()
|
||||
- st::groupCallMembersMargin.right();
|
||||
const auto muteTop = widget()->height() - 2 * _mute->height();
|
||||
const auto buttonsTop = muteTop;
|
||||
#ifdef Q_OS_WIN
|
||||
const auto membersTop = st::callTitleButton.height
|
||||
+ st::groupCallMembersMargin.top() / 2;
|
||||
#else // Q_OS_WIN
|
||||
const auto membersTop = st::groupCallMembersMargin.top();
|
||||
#endif // Q_OS_WIN
|
||||
const auto availableHeight = buttonsTop
|
||||
- membersTop
|
||||
- st::groupCallMembersMargin.bottom();
|
||||
_members->setGeometry(
|
||||
st::groupCallMembersMargin.left(),
|
||||
membersTop,
|
||||
membersWidth,
|
||||
std::min(desiredHeight, availableHeight));
|
||||
_mute->move((widget()->width() - _mute->width()) / 2, muteTop);
|
||||
_settings->moveToLeft(_settings->width(), buttonsTop);
|
||||
_hangup->moveToRight(_settings->width(), buttonsTop);
|
||||
}
|
||||
|
||||
void GroupPanel::paint(QRect clip) {
|
||||
|
@ -435,14 +400,16 @@ void GroupPanel::paint(QRect clip) {
|
|||
|
||||
auto region = QRegion(clip);
|
||||
for (const auto rect : region) {
|
||||
p.fillRect(rect, st::callBgOpaque);
|
||||
p.fillRect(rect, st::groupCallBg);
|
||||
}
|
||||
}
|
||||
|
||||
void GroupPanel::handleClose() {
|
||||
bool GroupPanel::handleClose() {
|
||||
if (_call) {
|
||||
_call->hangup();
|
||||
_window->hide();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
not_null<Ui::RpWidget*> GroupPanel::widget() const {
|
||||
|
|
|
@ -29,6 +29,7 @@ class FadeWrap;
|
|||
template <typename Widget>
|
||||
class PaddingWrap;
|
||||
class Window;
|
||||
class ScrollArea;
|
||||
namespace Platform {
|
||||
class TitleControls;
|
||||
} // namespace Platform
|
||||
|
@ -44,6 +45,8 @@ namespace Calls {
|
|||
class Userpic;
|
||||
class SignalBars;
|
||||
|
||||
class GroupMembers;
|
||||
|
||||
class GroupPanel final {
|
||||
public:
|
||||
GroupPanel(not_null<GroupCall*> call);
|
||||
|
@ -67,14 +70,11 @@ private:
|
|||
void initLayout();
|
||||
void initGeometry();
|
||||
|
||||
void handleClose();
|
||||
bool handleClose();
|
||||
|
||||
void updateControlsGeometry();
|
||||
void stateChanged(State state);
|
||||
void showControls();
|
||||
void startDurationUpdateTimer(crl::time currentDuration);
|
||||
|
||||
void toggleFullScreen(bool fullscreen);
|
||||
|
||||
GroupCall *_call = nullptr;
|
||||
not_null<ChannelData*> _channel;
|
||||
|
@ -87,6 +87,8 @@ private:
|
|||
|
||||
rpl::lifetime _callLifetime;
|
||||
|
||||
object_ptr<GroupMembers> _members;
|
||||
|
||||
object_ptr<Button> _settings;
|
||||
object_ptr<Button> _hangup;
|
||||
object_ptr<Button> _mute;
|
||||
|
|
|
@ -35,7 +35,7 @@ rpl::producer<Ui::GroupCallBarContent> GroupCallTracker::content() const {
|
|||
if (!call || (current && current->channel() == channel)) {
|
||||
return { .shown = false };
|
||||
} else if (!call->fullCount() && !call->participantsLoaded()) {
|
||||
call->requestParticipants();
|
||||
call->reload();
|
||||
}
|
||||
return { .count = call->fullCount(), .shown = true };
|
||||
});
|
||||
|
|
|
@ -517,22 +517,22 @@ historyUnreadBarFont: semiboldFont;
|
|||
historyForwardChooseMargins: margins(30px, 10px, 30px, 10px);
|
||||
historyForwardChooseFont: font(16px);
|
||||
|
||||
historyCallArrowIn: icon {{ "call_arrow_in", historyCallArrowInFg }};
|
||||
historyCallArrowInSelected: icon {{ "call_arrow_in", historyCallArrowInFgSelected }};
|
||||
historyCallArrowMissedIn: icon {{ "call_arrow_in", historyCallArrowMissedInFg }};
|
||||
historyCallArrowMissedInSelected: icon {{ "call_arrow_in", historyCallArrowMissedInFgSelected }};
|
||||
historyCallArrowOut: icon {{ "call_arrow_out", historyCallArrowOutFg }};
|
||||
historyCallArrowOutSelected: icon {{ "call_arrow_out", historyCallArrowOutFgSelected }};
|
||||
historyCallArrowIn: icon {{ "calls/call_arrow_in", historyCallArrowInFg }};
|
||||
historyCallArrowInSelected: icon {{ "calls/call_arrow_in", historyCallArrowInFgSelected }};
|
||||
historyCallArrowMissedIn: icon {{ "calls/call_arrow_in", historyCallArrowMissedInFg }};
|
||||
historyCallArrowMissedInSelected: icon {{ "calls/call_arrow_in", historyCallArrowMissedInFgSelected }};
|
||||
historyCallArrowOut: icon {{ "calls/call_arrow_out", historyCallArrowOutFg }};
|
||||
historyCallArrowOutSelected: icon {{ "calls/call_arrow_out", historyCallArrowOutFgSelected }};
|
||||
historyCallWidth: 240px;
|
||||
historyCallHeight: 56px;
|
||||
historyCallInIcon: icon {{ "call_answer", msgFileInBg }};
|
||||
historyCallInIconSelected: icon {{ "call_answer", msgFileInBgSelected }};
|
||||
historyCallOutIcon: icon {{ "call_answer", msgFileOutBg }};
|
||||
historyCallOutIconSelected: icon {{ "call_answer", msgFileOutBgSelected }};
|
||||
historyCallCameraInIcon: icon {{ "call_camera_active", msgFileInBg }};
|
||||
historyCallCameraInIconSelected: icon {{ "call_camera_active", msgFileInBgSelected }};
|
||||
historyCallCameraOutIcon: icon {{ "call_camera_active", msgFileOutBg }};
|
||||
historyCallCameraOutIconSelected: icon {{ "call_camera_active", msgFileOutBgSelected }};
|
||||
historyCallInIcon: icon {{ "calls/call_answer", msgFileInBg }};
|
||||
historyCallInIconSelected: icon {{ "calls/call_answer", msgFileInBgSelected }};
|
||||
historyCallOutIcon: icon {{ "calls/call_answer", msgFileOutBg }};
|
||||
historyCallOutIconSelected: icon {{ "calls/call_answer", msgFileOutBgSelected }};
|
||||
historyCallCameraInIcon: icon {{ "calls/call_camera_active", msgFileInBg }};
|
||||
historyCallCameraInIconSelected: icon {{ "calls/call_camera_active", msgFileInBgSelected }};
|
||||
historyCallCameraOutIcon: icon {{ "calls/call_camera_active", msgFileOutBg }};
|
||||
historyCallCameraOutIconSelected: icon {{ "calls/call_camera_active", msgFileOutBgSelected }};
|
||||
historyCallIconPosition: point(12px, 10px);
|
||||
historyCallLeft: 16px;
|
||||
historyCallTop: 9px;
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit f06346fbf03900c278e1d59717e1387bffc03f39
|
||||
Subproject commit e73bae12e26811f70adff5adfafbdb2548e02cd0
|