Implement menu in wide video mode.

This commit is contained in:
John Preston 2021-05-26 18:21:50 +04:00
parent 1c42513e44
commit 45cca35724
7 changed files with 194 additions and 78 deletions

View File

@ -739,6 +739,7 @@ groupCallJoinAsToggle: UserpicButton(defaultUserpicButton) {
photoPosition: point(3px, 3px);
}
groupCallMenuPosition: point(-1px, 29px);
groupCallWideMenuPosition: point(-2px, 28px);
groupCallActiveButton: IconButton {
width: 36px;
@ -848,6 +849,14 @@ groupCallScreenShareSmall: CallButton(groupCallSettingsSmall) {
rippleAreaPosition: point(8px, 12px);
}
}
groupCallMenuToggleSmall: CallButton(groupCallSettingsSmall) {
button: IconButton(groupCallSettingsInner) {
icon: icon {{ "calls/calls_more", groupCallIconFg }};
width: 60px;
height: 68px;
rippleAreaPosition: point(8px, 12px);
}
}
groupCallButtonSkip: 40px;
groupCallButtonSkipSmall: 5px;
groupCallButtonBottomSkip: 113px;

View File

@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_chat.h"
#include "data/data_channel.h"
#include "data/data_group_call.h"
#include "data/data_peer_values.h"
#include "data/data_session.h"
#include "base/global_shortcuts.h"
#include "base/openssl_help.h"
@ -364,6 +365,7 @@ GroupCall::GroupCall(
, _joinAs(info.joinAs)
, _possibleJoinAs(std::move(info.possibleJoinAs))
, _joinHash(info.joinHash)
, _canManage(Data::CanManageGroupCallValue(_peer))
, _id(inputCall.c_inputGroupCall().vid().v)
, _scheduleDate(info.scheduleDate)
, _lastSpokeCheckTimer([=] { checkLastSpoke(); })
@ -399,7 +401,7 @@ GroupCall::GroupCall(
if (const auto real = lookupReal()) {
subscribeToReal(real);
if (!_peer->canManageGroupCall() && real->joinMuted()) {
if (!canManage() && real->joinMuted()) {
_muted = MuteState::ForceMuted;
}
} else {
@ -472,6 +474,14 @@ bool GroupCall::mutedByAdmin() const {
return mute == MuteState::ForceMuted || mute == MuteState::RaisedHand;
}
bool GroupCall::canManage() const {
return _canManage.current();
}
rpl::producer<bool> GroupCall::canManageValue() const {
return _canManage.value();
}
void GroupCall::toggleVideo(bool active) {
if (!_instance
|| !_id
@ -758,6 +768,7 @@ void GroupCall::join(const MTPInputGroupCall &inputCall) {
_peerStream.events_starting_with_copy(_peer));
SubscribeToMigration(_peer, _lifetime, [=](not_null<ChannelData*> group) {
_peer = group;
_canManage = Data::CanManageGroupCallValue(_peer);
_peerStream.fire_copy(group);
});
}
@ -1124,7 +1135,7 @@ void GroupCall::applyParticipantLocally(
if (!participant || !participant->ssrc) {
return;
}
const auto canManageCall = _peer->canManageGroupCall();
const auto canManageCall = canManage();
const auto isMuted = participant->muted || (mute && canManageCall);
const auto canSelfUnmute = !canManageCall
? participant->canSelfUnmute
@ -1705,7 +1716,6 @@ void GroupCall::ensureControllerCreated() {
const auto weak = base::make_weak(&_instanceGuard);
const auto myLevel = std::make_shared<tgcalls::GroupLevelValue>();
_videoCall = true;
tgcalls::GroupInstanceDescriptor descriptor = {
.threads = tgcalls::StaticThreads::getThreads(),
.config = tgcalls::GroupConfig{

View File

@ -238,13 +238,6 @@ public:
return _muted.value();
}
[[nodiscard]] bool videoCall() const {
return _videoCall.current();
}
[[nodiscard]] rpl::producer<bool> videoCallValue() const {
return _videoCall.value();
}
[[nodiscard]] auto otherParticipantStateValue() const
-> rpl::producer<Group::ParticipantState>;
@ -328,6 +321,9 @@ public:
static constexpr auto kSpeakLevelThreshold = 0.2;
[[nodiscard]] bool mutedByAdmin() const;
[[nodiscard]] bool canManage() const;
[[nodiscard]] rpl::producer<bool> canManageValue() const;
void setCurrentAudioDevice(bool input, const QString &deviceId);
void setCurrentVideoDevice(const QString &deviceId);
[[nodiscard]] bool isSharingScreen() const;
@ -491,7 +487,7 @@ private:
QString _joinHash;
rpl::variable<MuteState> _muted = MuteState::Muted;
rpl::variable<bool> _videoCall = false;
rpl::variable<bool> _canManage = false;
bool _initialMuteStateSent = false;
bool _acceptFields = false;

View File

@ -590,7 +590,9 @@ void FillMenu(
not_null<Ui::DropdownMenu*> menu,
not_null<PeerData*> peer,
not_null<GroupCall*> call,
bool wide,
Fn<void()> chooseJoinAs,
Fn<void()> chooseShareScreenSource,
Fn<void(object_ptr<Ui::BoxContent>)> showBox) {
const auto weak = base::make_weak(call.get());
const auto resolveReal = [=] {
@ -606,9 +608,10 @@ void FillMenu(
}
const auto addEditJoinAs = call->showChooseJoinAs();
const auto addEditTitle = peer->canManageGroupCall();
const auto addEditRecording = peer->canManageGroupCall()
&& !real->scheduleDate();
const auto addEditTitle = call->canManage();
const auto addEditRecording = call->canManage() && !real->scheduleDate();
const auto addScreenCast = !wide
&& (real->canStartVideo() || call->isSharingScreen());
if (addEditJoinAs) {
menu->addAction(MakeJoinAsAction(
menu->menu(),
@ -660,6 +663,23 @@ void FillMenu(
real->recordStartDateValue(),
handler));
}
if (addScreenCast) {
const auto sharing = call->isSharingScreen();
const auto toggle = [=] {
if (const auto strong = weak.get()) {
if (sharing) {
strong->toggleScreenSharing(std::nullopt);
} else {
chooseShareScreenSource();
}
}
};
menu->addAction(
(call->isSharingScreen()
? tr::lng_group_call_screen_share_stop(tr::now)
: tr::lng_group_call_screen_share_start(tr::now)),
toggle);
}
menu->addAction(tr::lng_group_call_settings(tr::now), [=] {
if (const auto strong = weak.get()) {
showBox(Box(SettingsBox, strong));
@ -677,8 +697,12 @@ void FillMenu(
menu->addAction(MakeAttentionAction(
menu->menu(),
(real->scheduleDate()
? tr::lng_group_call_cancel(tr::now)
: tr::lng_group_call_end(tr::now)),
? (call->canManage()
? tr::lng_group_call_cancel(tr::now)
: tr::lng_group_call_leave(tr::now))
: (call->canManage()
? tr::lng_group_call_end(tr::now)
: tr::lng_group_call_leave(tr::now))),
finish));
}

View File

@ -61,7 +61,9 @@ void FillMenu(
not_null<Ui::DropdownMenu*> menu,
not_null<PeerData*> peer,
not_null<GroupCall*> call,
bool wide,
Fn<void()> chooseJoinAs,
Fn<void()> chooseShareScreenSource,
Fn<void(object_ptr<Ui::BoxContent>)> showBox);
[[nodiscard]] base::unique_qptr<Ui::Menu::ItemBase> MakeAttentionAction(

View File

@ -38,7 +38,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_group_call.h"
#include "data/data_session.h"
#include "data/data_changes.h"
#include "data/data_peer_values.h"
#include "main/main_session.h"
#include "base/event_filter.h"
#include "boxes/peers/edit_participants_box.h"
@ -555,9 +554,7 @@ void Panel::initWindow() {
: Flag::None;
});
rpl::combine(
_call->hasVideoWithFramesValue(),
_call->videoCallValue()
_call->hasVideoWithFramesValue(
) | rpl::start_with_next([=] {
updateMode();
}, _window->lifetime());
@ -584,7 +581,7 @@ void Panel::initWidget() {
}
void Panel::endCall() {
if (!_call->peer()->canManageGroupCall()) {
if (!_call->canManage()) {
_call->hangup();
return;
}
@ -626,7 +623,7 @@ void Panel::initControls() {
return (button == Qt::LeftButton);
}) | rpl::start_with_next([=] {
if (_call->scheduleDate()) {
if (_peer->canManageGroupCall()) {
if (_call->canManage()) {
startScheduledNow();
} else if (const auto real = _call->lookupReal()) {
_call->toggleScheduleStartSubscribed(
@ -649,6 +646,13 @@ void Panel::initControls() {
refreshLeftButton();
refreshVideoButtons();
rpl::combine(
_mode.value(),
_call->canManageValue()
) | rpl::start_with_next([=] {
refreshTopButton();
}, widget()->lifetime());
_hangup->setClickedCallback([=] { endCall(); });
const auto scheduleDate = _call->scheduleDate();
@ -730,6 +734,7 @@ void Panel::refreshLeftButton() {
const auto raw = _callShare ? _callShare.data() : _settings.data();
raw->show();
raw->setColorOverrides(_mute->colorOverrides());
updateButtonsStyles();
}
void Panel::refreshVideoButtons(std::optional<bool> overrideWideMode) {
@ -783,7 +788,7 @@ void Panel::refreshVideoButtons(std::optional<bool> overrideWideMode) {
_screenShare.create(widget(), st::groupCallScreenShareSmall);
_screenShare->show();
_screenShare->setClickedCallback([=] {
Ui::DesktopCapture::ChooseSource(this);
chooseShareScreenSource();
});
_screenShare->setColorOverrides(
toggleableOverrides(_call->isSharingScreenValue()));
@ -792,6 +797,13 @@ void Panel::refreshVideoButtons(std::optional<bool> overrideWideMode) {
_screenShare->setProgress(sharing ? 1. : 0.);
}, _screenShare->lifetime());
}
if (!_wideMenu) {
_wideMenu.create(widget(), st::groupCallMenuToggleSmall);
_wideMenu->show();
_wideMenu->setClickedCallback([=] { showMainMenu(); });
_wideMenu->setColorOverrides(
toggleableOverrides(_wideMenuShown.value()));
}
updateButtonsStyles();
updateButtonsGeometry();
}
@ -825,7 +837,7 @@ void Panel::setupRealMuteButtonState(not_null<Data::GroupCall*> real) {
_call->instanceStateValue(),
real->scheduleDateValue(),
real->scheduleStartSubscribedValue(),
Data::CanManageGroupCallValue(_peer),
_call->canManageValue(),
_mode.value()
) | rpl::distinct_until_changed(
) | rpl::filter(
@ -1103,6 +1115,7 @@ void Panel::raiseControls() {
&_settings,
&_callShare,
&_screenShare,
&_wideMenu,
&_video,
&_hangup
};
@ -1323,16 +1336,37 @@ void Panel::subscribeToChanges(not_null<Data::GroupCall*> real) {
}, widget()->lifetime());
validateRecordingMark(real->recordStartDate() != 0);
const auto showMenu = _peer->canManageGroupCall();
const auto showUserpic = !showMenu && _call->showChooseJoinAs();
if (showMenu) {
real->canStartVideoValue(
) | rpl::start_with_next([=] {
refreshVideoButtons();
refreshTopButton();
}, widget()->lifetime());
updateControlsGeometry();
}
void Panel::refreshTopButton() {
if (_mode.current() == PanelMode::Wide) {
_menuToggle.destroy();
_joinAsToggle.destroy();
updateButtonsGeometry(); // _wideMenu <-> _settings
return;
}
const auto real = _call->lookupReal();
const auto hasJoinAs = _call->showChooseJoinAs();
const auto wide = (_mode.current() == PanelMode::Wide);
const auto showNarrowMenu = _call->canManage()
|| (real && real->canStartVideo());
const auto showNarrowUserpic = !showNarrowMenu && hasJoinAs;
if (showNarrowMenu) {
_joinAsToggle.destroy();
if (!_menuToggle) {
_menuToggle.create(widget(), st::groupCallMenuToggle);
_menuToggle->show();
_menuToggle->setClickedCallback([=] { showMainMenu(); });
updateControlsGeometry();
}
} else if (showUserpic) {
} else if (showNarrowUserpic) {
_menuToggle.destroy();
rpl::single(
_call->joinAs()
@ -1357,12 +1391,10 @@ void Panel::subscribeToChanges(not_null<Data::GroupCall*> real) {
_menuToggle.destroy();
_joinAsToggle.destroy();
}
real->canStartVideoValue(
) | rpl::start_with_next([=] {
refreshVideoButtons();
}, widget()->lifetime());
}
updateControlsGeometry();
void Panel::chooseShareScreenSource() {
Ui::DesktopCapture::ChooseSource(this);
}
void Panel::chooseJoinAs() {
@ -1392,14 +1424,21 @@ void Panel::showMainMenu() {
if (_menu) {
return;
}
const auto wide = (_mode.current() == PanelMode::Wide) && _wideMenu;
if (!wide && !_menuToggle) {
return;
}
_menu.create(widget(), st::groupCallDropdownMenu);
FillMenu(
_menu.data(),
_peer,
_call,
wide,
[=] { chooseJoinAs(); },
[=] { chooseShareScreenSource(); },
[=](auto box) { _layerBg->showBox(std::move(box)); });
if (_menu->empty()) {
_wideMenuShown = false;
_menu.destroy();
return;
}
@ -1409,29 +1448,51 @@ void Panel::showMainMenu() {
raw->deleteLater();
if (_menu == raw) {
_menu = nullptr;
_menuToggle->setForceRippled(false);
_wideMenuShown = false;
_trackControlsMenuLifetime.destroy();
if (_menuToggle) {
_menuToggle->setForceRippled(false);
}
}
});
raw->setShowStartCallback([=] {
if (_menu == raw) {
_menuToggle->setForceRippled(true);
if (wide) {
_wideMenuShown = true;
} else if (_menuToggle) {
_menuToggle->setForceRippled(true);
}
}
});
raw->setHideStartCallback([=] {
if (_menu == raw) {
_menuToggle->setForceRippled(false);
_wideMenuShown = false;
if (_menuToggle) {
_menuToggle->setForceRippled(false);
}
}
});
_menuToggle->installEventFilter(_menu);
const auto x = st::groupCallMenuPosition.x();
const auto y = st::groupCallMenuPosition.y();
if (_menuToggle->x() > widget()->width() / 2) {
_menu->moveToRight(x, y);
_menu->showAnimated(Ui::PanelAnimation::Origin::TopRight);
if (wide) {
_wideMenu->installEventFilter(_menu);
const auto x = st::groupCallWideMenuPosition.x();
const auto y = st::groupCallWideMenuPosition.y();
_menu->moveToLeft(
_wideMenu->x() + x,
_wideMenu->y() - _menu->height() + y);
_menu->showAnimated(Ui::PanelAnimation::Origin::BottomLeft);
trackControl(_menu, _trackControlsMenuLifetime);
} else {
_menu->moveToLeft(x, y);
_menu->showAnimated(Ui::PanelAnimation::Origin::TopLeft);
_menuToggle->installEventFilter(_menu);
const auto x = st::groupCallMenuPosition.x();
const auto y = st::groupCallMenuPosition.y();
if (_menuToggle->x() > widget()->width() / 2) {
_menu->moveToRight(x, y);
_menu->showAnimated(Ui::PanelAnimation::Origin::TopRight);
} else {
_menu->moveToLeft(x, y);
_menu->showAnimated(Ui::PanelAnimation::Origin::TopLeft);
}
}
}
@ -1884,6 +1945,20 @@ void Panel::setupControlsBackgroundWide() {
trackControls(true);
}
template <typename WidgetPointer>
void Panel::trackControl(WidgetPointer &widget, rpl::lifetime &lifetime) {
if (widget) {
widget->events(
) | rpl::start_with_next([=](not_null<QEvent*> e) {
if (e->type() == QEvent::Enter) {
toggleWideControls(true);
} else if (e->type() == QEvent::Leave) {
toggleWideControls(false);
}
}, lifetime);
}
}
void Panel::trackControls(bool track) {
if (_trackControls == track) {
return;
@ -1892,6 +1967,7 @@ void Panel::trackControls(bool track) {
if (!track) {
_trackControlsLifetime.destroy();
_trackControlsOverStateLifetime.destroy();
_trackControlsMenuLifetime.destroy();
toggleWideControls(true);
if (_wideControlsAnimation.animating()) {
_wideControlsAnimation.stop();
@ -1901,24 +1977,16 @@ void Panel::trackControls(bool track) {
}
const auto trackOne = [=](auto &&widget) {
if (widget) {
const auto raw = &*widget;
raw->events(
) | rpl::start_with_next([=](not_null<QEvent*> e) {
if (e->type() == QEvent::Enter) {
toggleWideControls(true);
} else if (e->type() == QEvent::Leave) {
toggleWideControls(false);
}
}, _trackControlsOverStateLifetime);
}
trackControl(widget, _trackControlsOverStateLifetime);
};
trackOne(_mute);
trackOne(_video);
trackOne(_screenShare);
trackOne(_wideMenu);
trackOne(_settings);
trackOne(_hangup);
trackOne(_controlsBackgroundWide);
trackControl(_menu, _trackControlsMenuLifetime);
}
void Panel::updateControlsGeometry() {
@ -1956,28 +2024,21 @@ void Panel::updateButtonsGeometry() {
if (widget()->size().isEmpty() || (!_settings && !_callShare)) {
return;
}
const auto toggle = [&](bool shown) {
const auto toggleOne = [&](auto &widget) {
if (widget && widget->isHidden() == shown) {
widget->setVisible(shown);
}
};
toggleOne(_mute);
toggleOne(_video);
toggleOne(_screenShare);
toggleOne(_settings);
toggleOne(_callShare);
toggleOne(_hangup);
const auto toggle = [](auto &widget, bool shown) {
if (widget && widget->isHidden() == shown) {
widget->setVisible(shown);
}
};
if (mode() == PanelMode::Wide) {
Assert(_video != nullptr);
Assert(_screenShare != nullptr);
Assert(_wideMenu != nullptr);
Assert(_settings != nullptr);
Assert(_callShare == nullptr);
const auto shown = _wideControlsAnimation.value(
_wideControlsShown ? 1. : 0.);
toggle(shown > 0.);
const auto hidden = (shown == 0.);
if (_viewport) {
_viewport->setControlsShown(shown);
@ -2002,17 +2063,23 @@ void Panel::updateButtonsGeometry() {
- membersWidth
- membersSkip
- fullWidth) / 2;
_screenShare->setVisible(true);
toggle(_screenShare, !hidden);
_screenShare->moveToLeft(left, buttonsTop);
left += _screenShare->width() + skip;
_screenShare->setVisible(true);
toggle(_video, !hidden);
_video->moveToLeft(left, buttonsTop);
left += _video->width() + skip;
toggle(_mute, !hidden);
_mute->moveInner({ left + addSkip, buttonsTop + addSkip });
left += muteSize + skip;
_settings->setVisible(true);
const auto wideMenuShown = _call->canManage()
|| _call->showChooseJoinAs();
toggle(_settings, !hidden && !wideMenuShown);
toggle(_wideMenu, !hidden && wideMenuShown);
_wideMenu->moveToLeft(left, buttonsTop);
_settings->moveToLeft(left, buttonsTop);
left += _settings->width() + skip;
toggle(_hangup, !hidden);
_hangup->moveToLeft(left, buttonsTop);
left += _hangup->width();
if (_controlsBackgroundWide) {
@ -2025,8 +2092,6 @@ void Panel::updateButtonsGeometry() {
rect.marginsAdded(st::groupCallControlsBackMargin));
}
} else {
toggle(true);
const auto muteTop = widget()->height()
- st::groupCallMuteBottomSkip;
const auto buttonsTop = widget()->height()
@ -2035,24 +2100,26 @@ void Panel::updateButtonsGeometry() {
const auto fullWidth = muteSize
+ 2 * (_settings ? _settings : _callShare)->width()
+ 2 * st::groupCallButtonSkip;
toggle(_mute, true);
_mute->moveInner({ (widget()->width() - muteSize) / 2, muteTop });
const auto leftButtonLeft = (widget()->width() - fullWidth) / 2;
if (_screenShare) {
_screenShare->setVisible(false);
}
toggle(_screenShare, false);
toggle(_wideMenu, false);
toggle(_callShare, true);
if (_callShare) {
_callShare->moveToLeft(leftButtonLeft, buttonsTop);
}
const auto showVideoButton = videoButtonInNarrowMode();
toggle(_video, !_callShare && showVideoButton);
if (_video) {
_video->setVisible(!_callShare && showVideoButton);
_video->setStyle(st::groupCallVideo, &st::groupCallVideoActive);
_video->moveToLeft(leftButtonLeft, buttonsTop);
}
toggle(_settings, !_callShare && !showVideoButton);
if (_settings) {
_settings->setVisible(!_callShare && !showVideoButton);
_settings->moveToLeft(leftButtonLeft, buttonsTop);
}
toggle(_hangup, true);
_hangup->moveToRight(leftButtonLeft, buttonsTop);
}
if (_controlsBackgroundNarrow) {

View File

@ -99,6 +99,9 @@ private:
void enlargeVideo();
void minimizeVideo();
template <typename WidgetPointer>
void trackControl(WidgetPointer &widget, rpl::lifetime &lifetime);
bool updateMode();
void updateControlsGeometry();
void updateButtonsGeometry();
@ -111,6 +114,7 @@ private:
void refreshLeftButton();
void refreshVideoButtons(
std::optional<bool> overrideWideMode = std::nullopt);
void refreshTopButton();
void toggleWideControls(bool shown);
[[nodiscard]] bool videoButtonInNarrowMode() const;
@ -118,6 +122,7 @@ private:
void showMainMenu();
void chooseJoinAs();
void chooseShareScreenSource();
void addMembers();
void kickParticipant(not_null<PeerData*> participantPeer);
void kickParticipantSure(not_null<PeerData*> participantPeer);
@ -154,11 +159,13 @@ private:
object_ptr<Ui::AbstractButton> _recordingMark = { nullptr };
object_ptr<Ui::IconButton> _menuToggle = { nullptr };
object_ptr<Ui::DropdownMenu> _menu = { nullptr };
rpl::variable<bool> _wideMenuShown = false;
object_ptr<Ui::AbstractButton> _joinAsToggle = { nullptr };
object_ptr<Members> _members = { nullptr };
std::unique_ptr<Viewport> _viewport;
rpl::lifetime _trackControlsLifetime;
rpl::lifetime _trackControlsOverStateLifetime;
rpl::lifetime _trackControlsMenuLifetime;
object_ptr<Ui::FlatLabel> _startsIn = { nullptr };
object_ptr<Ui::RpWidget> _countdown = { nullptr };
std::shared_ptr<Ui::GroupCallScheduledLeft> _countdownData;
@ -175,6 +182,7 @@ private:
object_ptr<Ui::RpWidget> _controlsBackgroundWide = { nullptr };
std::unique_ptr<ControlsBackgroundNarrow> _controlsBackgroundNarrow;
object_ptr<Ui::CallButton> _settings = { nullptr };
object_ptr<Ui::CallButton> _wideMenu = { nullptr };
object_ptr<Ui::CallButton> _callShare = { nullptr };
object_ptr<Ui::CallButton> _video = { nullptr };
object_ptr<Ui::CallButton> _screenShare = { nullptr };