mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Toggle fullscreen by mute button in RTMP streams.
This commit is contained in:
parent
25e29d3dd5
commit
fc5ed46b40
11 changed files with 405 additions and 308 deletions
|
@ -270,6 +270,9 @@ callMuteMajorBlobMinRadius: 67px;
|
|||
callMuteMajorBlobMaxRadius: 77px;
|
||||
callMuteBlobRadiusForDiameter: 100px;
|
||||
|
||||
callMuteToFullScreen: icon {{ "player/player_fullscreen", groupCallIconFg }};
|
||||
callMuteFromFullScreen: icon {{ "player/player_minimize", groupCallIconFg }};
|
||||
|
||||
callConnectingRadial: InfiniteRadialAnimation(defaultInfiniteRadialAnimation) {
|
||||
color: lightButtonFg;
|
||||
thickness: 4px;
|
||||
|
@ -489,6 +492,7 @@ callErrorToast: Toast(defaultToast) {
|
|||
|
||||
groupCallWidth: 380px;
|
||||
groupCallHeight: 580px;
|
||||
groupCallHeightRtmpMin: 280px;
|
||||
|
||||
groupCallRipple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: groupCallMembersBgRipple;
|
||||
|
|
|
@ -990,9 +990,10 @@ void GroupCall::playConnectingSoundOnce() {
|
|||
}
|
||||
|
||||
bool GroupCall::showChooseJoinAs() const {
|
||||
return (_possibleJoinAs.size() > 1)
|
||||
|| (_possibleJoinAs.size() == 1
|
||||
&& !_possibleJoinAs.front()->isSelf());
|
||||
return !_rtmp
|
||||
&& ((_possibleJoinAs.size() > 1)
|
||||
|| (_possibleJoinAs.size() == 1
|
||||
&& !_possibleJoinAs.front()->isSelf()));
|
||||
}
|
||||
|
||||
bool GroupCall::scheduleStartSubscribed() const {
|
||||
|
@ -1010,6 +1011,14 @@ bool GroupCall::listenersHidden() const {
|
|||
return _listenersHidden;
|
||||
}
|
||||
|
||||
bool GroupCall::emptyRtmp() const {
|
||||
return _emptyRtmp.current();
|
||||
}
|
||||
|
||||
rpl::producer<bool> GroupCall::emptyRtmpValue() const {
|
||||
return _emptyRtmp.value();
|
||||
}
|
||||
|
||||
Data::GroupCall *GroupCall::lookupReal() const {
|
||||
const auto real = _peer->groupCall();
|
||||
return (real && real->id() == _id) ? real : nullptr;
|
||||
|
@ -2601,7 +2610,8 @@ void GroupCall::requestCurrentTimeStart(
|
|||
).done([=](const MTPphone_GroupCallStreamChannels &result) {
|
||||
result.match([&](const MTPDphone_groupCallStreamChannels &data) {
|
||||
const auto &list = data.vchannels().v;
|
||||
if (!list.isEmpty()) {
|
||||
const auto empty = list.isEmpty();
|
||||
if (!empty) {
|
||||
const auto &first = list.front();
|
||||
first.match([&](const MTPDgroupCallStreamChannel &data) {
|
||||
finish(data.vlast_timestamp_ms().v);
|
||||
|
@ -2609,6 +2619,7 @@ void GroupCall::requestCurrentTimeStart(
|
|||
} else {
|
||||
finish(0);
|
||||
}
|
||||
_emptyRtmp = empty;
|
||||
});
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
finish(0);
|
||||
|
|
|
@ -233,6 +233,8 @@ public:
|
|||
[[nodiscard]] bool scheduleStartSubscribed() const;
|
||||
[[nodiscard]] bool rtmp() const;
|
||||
[[nodiscard]] bool listenersHidden() const;
|
||||
[[nodiscard]] bool emptyRtmp() const;
|
||||
[[nodiscard]] rpl::producer<bool> emptyRtmpValue() const;
|
||||
|
||||
[[nodiscard]] Data::GroupCall *lookupReal() const;
|
||||
[[nodiscard]] rpl::producer<not_null<Data::GroupCall*>> real() const;
|
||||
|
@ -593,6 +595,7 @@ private:
|
|||
rpl::variable<MuteState> _muted = MuteState::Muted;
|
||||
rpl::variable<bool> _canManage = false;
|
||||
rpl::variable<bool> _videoIsWorking = false;
|
||||
rpl::variable<bool> _emptyRtmp = false;
|
||||
bool _initialMuteStateSent = false;
|
||||
bool _acceptFields = false;
|
||||
|
||||
|
|
|
@ -1191,7 +1191,8 @@ base::unique_qptr<Ui::PopupMenu> Members::Controller::createRowContextMenu(
|
|||
const auto muted = (muteState == Row::State::Muted)
|
||||
|| (muteState == Row::State::RaisedHand);
|
||||
const auto addCover = true;
|
||||
const auto addVolumeItem = !muted || isMe(participantPeer);
|
||||
const auto addVolumeItem = !_call->rtmp()
|
||||
&& (!muted || isMe(participantPeer));
|
||||
const auto admin = IsGroupCallAdmin(_peer, participantPeer);
|
||||
const auto session = &_peer->session();
|
||||
const auto getCurrentWindow = [=]() -> Window::SessionController* {
|
||||
|
@ -1430,7 +1431,8 @@ void Members::Controller::addMuteActionsToContextMenu(
|
|||
|
||||
auto mutesFromVolume = rpl::never<bool>() | rpl::type_erased();
|
||||
|
||||
const auto addVolumeItem = !muted || isMe(participantPeer);
|
||||
const auto addVolumeItem = !_call->rtmp()
|
||||
&& (!muted || isMe(participantPeer));
|
||||
if (addVolumeItem) {
|
||||
auto otherParticipantStateValue
|
||||
= _call->otherParticipantStateValue(
|
||||
|
@ -1492,6 +1494,7 @@ void Members::Controller::addMuteActionsToContextMenu(
|
|||
|
||||
const auto muteAction = [&]() -> QAction* {
|
||||
if (muteState == Row::State::Invited
|
||||
|| _call->rtmp()
|
||||
|| isMe(participantPeer)
|
||||
|| (muteState == Row::State::Inactive
|
||||
&& participantIsCallAdmin
|
||||
|
|
|
@ -114,6 +114,9 @@ Panel::Panel(not_null<GroupCall*> call)
|
|||
: _call->scheduleStartSubscribed()
|
||||
? Ui::CallMuteButtonType::ScheduledNotify
|
||||
: Ui::CallMuteButtonType::ScheduledSilent),
|
||||
.expandType = ((_call->scheduleDate() || !_call->rtmp())
|
||||
? Ui::CallMuteButtonExpandType::None
|
||||
: Ui::CallMuteButtonExpandType::Normal),
|
||||
}))
|
||||
, _hangup(widget(), st::groupCallHangup)
|
||||
, _stickedTooltipsShown(Core::App().settings().hiddenGroupCallTooltips()
|
||||
|
@ -257,22 +260,34 @@ void Panel::initWindow() {
|
|||
return base::EventFilterResult::Cancel;
|
||||
} else if (e->type() == QEvent::KeyPress
|
||||
|| e->type() == QEvent::KeyRelease) {
|
||||
if (static_cast<QKeyEvent*>(e.get())->key() == Qt::Key_Space) {
|
||||
const auto key = static_cast<QKeyEvent*>(e.get())->key();
|
||||
if (key == Qt::Key_Space) {
|
||||
_call->pushToTalk(
|
||||
e->type() == QEvent::KeyPress,
|
||||
kSpacePushToTalkDelay);
|
||||
} else if (key == Qt::Key_Escape && _fullScreen.current()) {
|
||||
toggleFullScreen(false);
|
||||
}
|
||||
}
|
||||
return base::EventFilterResult::Continue;
|
||||
});
|
||||
|
||||
QObject::connect(
|
||||
window()->windowHandle(),
|
||||
&QWindow::windowStateChanged,
|
||||
[=](Qt::WindowState state) {
|
||||
_fullScreen = (state == Qt::WindowFullScreen);
|
||||
});
|
||||
|
||||
window()->setBodyTitleArea([=](QPoint widgetPoint) {
|
||||
using Flag = Ui::WindowTitleHitTestFlag;
|
||||
const auto titleRect = QRect(
|
||||
0,
|
||||
0,
|
||||
widget()->width(),
|
||||
st::groupCallMembersTop);
|
||||
(mode() == PanelMode::Wide
|
||||
? st::groupCallWideVideoTop
|
||||
: st::groupCallMembersTop));
|
||||
const auto moveable = (titleRect.contains(widgetPoint)
|
||||
&& (!_menuToggle || !_menuToggle->geometry().contains(widgetPoint))
|
||||
&& (!_menu || !_menu->geometry().contains(widgetPoint))
|
||||
|
@ -365,7 +380,11 @@ void Panel::initControls() {
|
|||
!real->scheduleStartSubscribed());
|
||||
}
|
||||
return;
|
||||
} else if (_call->rtmp()) {
|
||||
toggleFullScreen(!_fullScreen.current());
|
||||
return;
|
||||
}
|
||||
|
||||
const auto oldState = _call->muted();
|
||||
const auto newState = (oldState == MuteState::ForceMuted)
|
||||
? MuteState::RaisedHand
|
||||
|
@ -450,6 +469,14 @@ void Panel::initControls() {
|
|||
refreshControlsBackground();
|
||||
}
|
||||
|
||||
void Panel::toggleFullScreen(bool fullscreen) {
|
||||
if (fullscreen) {
|
||||
window()->showFullScreen();
|
||||
} else {
|
||||
window()->showNormal();
|
||||
}
|
||||
}
|
||||
|
||||
void Panel::refreshLeftButton() {
|
||||
const auto share = _call->scheduleDate()
|
||||
&& _peer->isBroadcast()
|
||||
|
@ -467,6 +494,7 @@ void Panel::refreshLeftButton() {
|
|||
_settings->setClickedCallback([=] {
|
||||
showBox(Box(SettingsBox, _call));
|
||||
});
|
||||
trackControls(_trackControls, true);
|
||||
}
|
||||
const auto raw = _callShare ? _callShare.data() : _settings.data();
|
||||
raw->show();
|
||||
|
@ -615,7 +643,8 @@ void Panel::setupRealMuteButtonState(not_null<Data::GroupCall*> real) {
|
|||
real->scheduleDateValue(),
|
||||
real->scheduleStartSubscribedValue(),
|
||||
_call->canManageValue(),
|
||||
_mode.value()
|
||||
_mode.value(),
|
||||
_fullScreen.value()
|
||||
) | rpl::distinct_until_changed(
|
||||
) | rpl::filter(
|
||||
_2 != GroupCall::InstanceState::TransitionToRtc
|
||||
|
@ -625,9 +654,11 @@ void Panel::setupRealMuteButtonState(not_null<Data::GroupCall*> real) {
|
|||
TimeId scheduleDate,
|
||||
bool scheduleStartSubscribed,
|
||||
bool canManage,
|
||||
PanelMode mode) {
|
||||
PanelMode mode,
|
||||
bool fullScreen) {
|
||||
const auto wide = (mode == PanelMode::Wide);
|
||||
using Type = Ui::CallMuteButtonType;
|
||||
using ExpandType = Ui::CallMuteButtonExpandType;
|
||||
_mute->setState(Ui::CallMuteButtonState{
|
||||
.text = (wide
|
||||
? QString()
|
||||
|
@ -664,6 +695,11 @@ void Panel::setupRealMuteButtonState(not_null<Data::GroupCall*> real) {
|
|||
: mute == MuteState::Muted
|
||||
? Type::Muted
|
||||
: Type::Active),
|
||||
.expandType = ((scheduleDate || !_call->rtmp())
|
||||
? ExpandType::None
|
||||
: fullScreen
|
||||
? ExpandType::Expanded
|
||||
: ExpandType::Normal),
|
||||
});
|
||||
}, _callLifetime);
|
||||
}
|
||||
|
@ -860,8 +896,10 @@ void Panel::minimizeVideo() {
|
|||
}
|
||||
const auto available = window()->screen()->availableGeometry();
|
||||
const auto width = st::groupCallWidth;
|
||||
const auto height = st::groupCallHeight;
|
||||
auto geometry = QRect(
|
||||
const auto height = _call->rtmp()
|
||||
? st::groupCallHeightRtmpMin
|
||||
: st::groupCallHeight;
|
||||
const auto geometry = QRect(
|
||||
window()->x() + (window()->width() - width) / 2,
|
||||
window()->y() + (window()->height() - height) / 2,
|
||||
width,
|
||||
|
@ -1277,13 +1315,14 @@ void Panel::showMainMenu() {
|
|||
|
||||
if (wide) {
|
||||
_wideMenu->installEventFilter(_menu);
|
||||
trackControl(_menu, _trackControlsMenuLifetime);
|
||||
|
||||
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 {
|
||||
_menuToggle->installEventFilter(_menu);
|
||||
const auto x = st::groupCallMenuPosition.x();
|
||||
|
@ -1404,7 +1443,10 @@ rpl::lifetime &Panel::lifetime() {
|
|||
|
||||
void Panel::initGeometry() {
|
||||
const auto center = Core::App().getPointForCallPanelCenter();
|
||||
const auto rect = QRect(0, 0, st::groupCallWidth, st::groupCallHeight);
|
||||
const auto height = (_call->rtmp() && !_call->canManage())
|
||||
? st::groupCallHeightRtmpMin
|
||||
: st::groupCallHeight;
|
||||
const auto rect = QRect(0, 0, st::groupCallWidth, height);
|
||||
window()->setGeometry(rect.translated(center - rect.center()));
|
||||
window()->setMinimumSize(rect.size());
|
||||
window()->show();
|
||||
|
@ -1854,15 +1896,14 @@ void Panel::updateTooltipGeometry() {
|
|||
_niceTooltip->pointAt(geometry, RectPart::Top, countPosition);
|
||||
}
|
||||
|
||||
void Panel::trackControls(bool track) {
|
||||
if (_trackControls == track) {
|
||||
void Panel::trackControls(bool track, bool force) {
|
||||
if (!force && _trackControls == track) {
|
||||
return;
|
||||
}
|
||||
_trackControls = track;
|
||||
_trackControlsOverStateLifetime.destroy();
|
||||
_trackControlsMenuLifetime.destroy();
|
||||
if (!track) {
|
||||
_trackControlsLifetime.destroy();
|
||||
_trackControlsOverStateLifetime.destroy();
|
||||
_trackControlsMenuLifetime.destroy();
|
||||
toggleWideControls(true);
|
||||
if (_wideControlsAnimation.animating()) {
|
||||
_wideControlsAnimation.stop();
|
||||
|
@ -1932,12 +1973,13 @@ void Panel::updateButtonsGeometry() {
|
|||
Assert(_settings != nullptr);
|
||||
Assert(_callShare == nullptr);
|
||||
|
||||
const auto rtmp = _call->rtmp();
|
||||
const auto shown = _wideControlsAnimation.value(
|
||||
_wideControlsShown ? 1. : 0.);
|
||||
const auto hidden = (shown == 0.);
|
||||
|
||||
if (_viewport) {
|
||||
_viewport->setControlsShown(_call->rtmp() ? 0. : shown);
|
||||
_viewport->setControlsShown(rtmp ? 0. : shown);
|
||||
}
|
||||
|
||||
const auto buttonsTop = widget()->height() - anim::interpolate(
|
||||
|
@ -1947,10 +1989,10 @@ void Panel::updateButtonsGeometry() {
|
|||
const auto addSkip = st::callMuteButtonSmall.active.outerRadius;
|
||||
const auto muteSize = _mute->innerSize().width() + 2 * addSkip;
|
||||
const auto skip = st::groupCallButtonSkipSmall;
|
||||
const auto fullWidth = (_video->width() + skip)
|
||||
+ (_screenShare->width() + skip)
|
||||
const auto fullWidth = (rtmp ? 0 : (_video->width() + skip))
|
||||
+ (rtmp ? 0 : (_screenShare->width() + skip))
|
||||
+ (muteSize + skip)
|
||||
+ (_settings ->width() + skip)
|
||||
+ (_settings->width() + skip)
|
||||
+ _hangup->width();
|
||||
const auto membersSkip = st::groupCallNarrowSkip;
|
||||
const auto membersWidth = _call->rtmp()
|
||||
|
@ -1960,12 +2002,20 @@ void Panel::updateButtonsGeometry() {
|
|||
- membersWidth
|
||||
- membersSkip
|
||||
- fullWidth) / 2;
|
||||
toggle(_screenShare, !hidden);
|
||||
_screenShare->moveToLeft(left, buttonsTop);
|
||||
left += _screenShare->width() + skip;
|
||||
toggle(_video, !hidden);
|
||||
_video->moveToLeft(left, buttonsTop);
|
||||
left += _video->width() + skip;
|
||||
toggle(_screenShare, !hidden && !rtmp);
|
||||
if (!rtmp) {
|
||||
_screenShare->moveToLeft(left, buttonsTop);
|
||||
left += _screenShare->width() + skip;
|
||||
}
|
||||
toggle(_video, !hidden && !rtmp);
|
||||
if (!rtmp) {
|
||||
_video->moveToLeft(left, buttonsTop);
|
||||
left += _video->width() + skip;
|
||||
} else {
|
||||
_wideMenu->moveToLeft(left, buttonsTop);
|
||||
_settings->moveToLeft(left, buttonsTop);
|
||||
left += _settings->width() + skip;
|
||||
}
|
||||
toggle(_mute, !hidden);
|
||||
_mute->moveInner({ left + addSkip, buttonsTop + addSkip });
|
||||
left += muteSize + skip;
|
||||
|
@ -1973,9 +2023,11 @@ void Panel::updateButtonsGeometry() {
|
|||
|| _call->showChooseJoinAs();
|
||||
toggle(_settings, !hidden && !wideMenuShown);
|
||||
toggle(_wideMenu, !hidden && wideMenuShown);
|
||||
_wideMenu->moveToLeft(left, buttonsTop);
|
||||
_settings->moveToLeft(left, buttonsTop);
|
||||
left += _settings->width() + skip;
|
||||
if (!rtmp) {
|
||||
_wideMenu->moveToLeft(left, buttonsTop);
|
||||
_settings->moveToLeft(left, buttonsTop);
|
||||
left += _settings->width() + skip;
|
||||
}
|
||||
toggle(_hangup, !hidden);
|
||||
_hangup->moveToLeft(left, buttonsTop);
|
||||
left += _hangup->width();
|
||||
|
|
|
@ -130,7 +130,8 @@ private:
|
|||
|
||||
bool handleClose();
|
||||
void startScheduledNow();
|
||||
void trackControls(bool track);
|
||||
void toggleFullScreen(bool fullscreen);
|
||||
void trackControls(bool track, bool force = false);
|
||||
void raiseControls();
|
||||
void enlargeVideo();
|
||||
void minimizeVideo();
|
||||
|
@ -197,6 +198,7 @@ private:
|
|||
Ui::GL::Window _window;
|
||||
const std::unique_ptr<Ui::LayerManager> _layerBg;
|
||||
rpl::variable<PanelMode> _mode;
|
||||
rpl::variable<bool> _fullScreen = false;
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
std::unique_ptr<Ui::Platform::SeparateTitleControls> _controls;
|
||||
|
@ -215,7 +217,6 @@ private:
|
|||
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 };
|
||||
|
|
|
@ -235,6 +235,7 @@ void SettingsBox(
|
|||
const auto peer = call->peer();
|
||||
const auto state = box->lifetime().make_state<State>();
|
||||
const auto real = peer->groupCall();
|
||||
const auto rtmp = call->rtmp();
|
||||
const auto id = call->id();
|
||||
const auto goodReal = (real && real->id() == id);
|
||||
|
||||
|
@ -274,280 +275,280 @@ void SettingsBox(
|
|||
}), &st::groupCallCheckbox, &st::groupCallRadio));
|
||||
});
|
||||
|
||||
AddButtonWithLabel(
|
||||
layout,
|
||||
tr::lng_group_call_microphone(),
|
||||
rpl::single(
|
||||
CurrentAudioInputName()
|
||||
) | rpl::then(
|
||||
state->inputNameStream.events()
|
||||
),
|
||||
st::groupCallSettingsButton
|
||||
)->addClickHandler([=] {
|
||||
box->getDelegate()->show(ChooseAudioInputBox(crl::guard(box, [=](
|
||||
const QString &id,
|
||||
const QString &name) {
|
||||
state->inputNameStream.fire_copy(name);
|
||||
if (state->micTester) {
|
||||
state->micTester->setDeviceId(id);
|
||||
}
|
||||
}), &st::groupCallCheckbox, &st::groupCallRadio));
|
||||
});
|
||||
|
||||
state->micTestLevel = box->addRow(
|
||||
object_ptr<Ui::LevelMeter>(
|
||||
box.get(),
|
||||
st::groupCallLevelMeter),
|
||||
st::settingsLevelMeterPadding);
|
||||
state->micTestLevel->resize(QSize(0, st::defaultLevelMeter.height));
|
||||
|
||||
state->levelUpdateTimer.setCallback([=] {
|
||||
const auto was = state->micLevel;
|
||||
state->micLevel = state->micTester->getAndResetLevel();
|
||||
state->micLevelAnimation.start([=] {
|
||||
state->micTestLevel->setValue(
|
||||
state->micLevelAnimation.value(state->micLevel));
|
||||
}, was, state->micLevel, kMicTestAnimationDuration);
|
||||
});
|
||||
|
||||
AddSkip(layout);
|
||||
//AddDivider(layout);
|
||||
//AddSkip(layout);
|
||||
|
||||
AddButton(
|
||||
layout,
|
||||
tr::lng_group_call_noise_suppression(),
|
||||
st::groupCallSettingsButton
|
||||
)->toggleOn(rpl::single(
|
||||
settings.groupCallNoiseSuppression()
|
||||
))->toggledChanges(
|
||||
) | rpl::start_with_next([=](bool enabled) {
|
||||
Core::App().settings().setGroupCallNoiseSuppression(enabled);
|
||||
call->setNoiseSuppression(enabled);
|
||||
Core::App().saveSettingsDelayed();
|
||||
}, layout->lifetime());
|
||||
|
||||
|
||||
using GlobalShortcut = base::GlobalShortcut;
|
||||
struct PushToTalkState {
|
||||
rpl::variable<QString> recordText = tr::lng_group_call_ptt_shortcut();
|
||||
rpl::variable<QString> shortcutText;
|
||||
rpl::event_stream<bool> pushToTalkToggles;
|
||||
std::shared_ptr<base::GlobalShortcutManager> manager;
|
||||
GlobalShortcut shortcut;
|
||||
crl::time delay = 0;
|
||||
bool recording = false;
|
||||
};
|
||||
if (base::GlobalShortcutsAvailable()) {
|
||||
const auto state = box->lifetime().make_state<PushToTalkState>();
|
||||
if (!base::GlobalShortcutsAllowed()) {
|
||||
Core::App().settings().setGroupCallPushToTalk(false);
|
||||
}
|
||||
const auto tryFillFromManager = [=] {
|
||||
state->shortcut = state->manager
|
||||
? state->manager->shortcutFromSerialized(
|
||||
Core::App().settings().groupCallPushToTalkShortcut())
|
||||
: nullptr;
|
||||
state->shortcutText = state->shortcut
|
||||
? state->shortcut->toDisplayString()
|
||||
: QString();
|
||||
};
|
||||
state->manager = settings.groupCallPushToTalk()
|
||||
? call->ensureGlobalShortcutManager()
|
||||
: nullptr;
|
||||
tryFillFromManager();
|
||||
|
||||
state->delay = settings.groupCallPushToTalkDelay();
|
||||
const auto pushToTalk = AddButton(
|
||||
if (!rtmp) {
|
||||
AddButtonWithLabel(
|
||||
layout,
|
||||
tr::lng_group_call_push_to_talk(),
|
||||
tr::lng_group_call_microphone(),
|
||||
rpl::single(
|
||||
CurrentAudioInputName()
|
||||
) | rpl::then(
|
||||
state->inputNameStream.events()
|
||||
),
|
||||
st::groupCallSettingsButton
|
||||
)->toggleOn(rpl::single(
|
||||
settings.groupCallPushToTalk()
|
||||
) | rpl::then(state->pushToTalkToggles.events()));
|
||||
const auto pushToTalkWrap = layout->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
layout,
|
||||
object_ptr<Ui::VerticalLayout>(layout)));
|
||||
const auto pushToTalkInner = pushToTalkWrap->entity();
|
||||
const auto recording = AddButton(
|
||||
pushToTalkInner,
|
||||
state->recordText.value(),
|
||||
st::groupCallSettingsButton);
|
||||
CreateRightLabel(
|
||||
recording,
|
||||
state->shortcutText.value(),
|
||||
st::groupCallSettingsButton,
|
||||
state->recordText.value());
|
||||
|
||||
const auto applyAndSave = [=] {
|
||||
call->applyGlobalShortcutChanges();
|
||||
Core::App().saveSettingsDelayed();
|
||||
};
|
||||
const auto showPrivacyRequest = [=] {
|
||||
#ifdef Q_OS_MAC
|
||||
if (!Platform::IsMac10_14OrGreater()) {
|
||||
return;
|
||||
}
|
||||
const auto requestInputMonitoring = Platform::IsMac10_15OrGreater();
|
||||
box->getDelegate()->show(Box([=](not_null<Ui::GenericBox*> box) {
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box.get(),
|
||||
rpl::combine(
|
||||
tr::lng_group_call_mac_access(),
|
||||
(requestInputMonitoring
|
||||
? tr::lng_group_call_mac_input()
|
||||
: tr::lng_group_call_mac_accessibility())
|
||||
) | rpl::map([](QString a, QString b) {
|
||||
auto result = Ui::Text::RichLangValue(a);
|
||||
result.append("\n\n").append(Ui::Text::RichLangValue(b));
|
||||
return result;
|
||||
}),
|
||||
st::groupCallBoxLabel),
|
||||
style::margins(
|
||||
st::boxRowPadding.left(),
|
||||
st::boxPadding.top(),
|
||||
st::boxRowPadding.right(),
|
||||
st::boxPadding.bottom()));
|
||||
box->addButton(tr::lng_group_call_mac_settings(), [=] {
|
||||
if (requestInputMonitoring) {
|
||||
Platform::OpenInputMonitoringPrivacySettings();
|
||||
} else {
|
||||
Platform::OpenAccessibilityPrivacySettings();
|
||||
}
|
||||
});
|
||||
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||
|
||||
if (!requestInputMonitoring) {
|
||||
// Accessibility is enabled without app restart, so short-poll it.
|
||||
base::timer_each(
|
||||
kCheckAccessibilityInterval
|
||||
) | rpl::filter([] {
|
||||
return base::GlobalShortcutsAllowed();
|
||||
}) | rpl::start_with_next([=] {
|
||||
box->closeBox();
|
||||
}, box->lifetime());
|
||||
)->addClickHandler([=] {
|
||||
box->getDelegate()->show(ChooseAudioInputBox(crl::guard(box, [=](
|
||||
const QString &id,
|
||||
const QString &name) {
|
||||
state->inputNameStream.fire_copy(name);
|
||||
if (state->micTester) {
|
||||
state->micTester->setDeviceId(id);
|
||||
}
|
||||
}));
|
||||
#endif // Q_OS_MAC
|
||||
};
|
||||
const auto ensureManager = [=] {
|
||||
if (state->manager) {
|
||||
return true;
|
||||
} else if (base::GlobalShortcutsAllowed()) {
|
||||
state->manager = call->ensureGlobalShortcutManager();
|
||||
tryFillFromManager();
|
||||
return true;
|
||||
}
|
||||
showPrivacyRequest();
|
||||
return false;
|
||||
};
|
||||
const auto stopRecording = [=] {
|
||||
state->recording = false;
|
||||
state->recordText = tr::lng_group_call_ptt_shortcut();
|
||||
state->shortcutText = state->shortcut
|
||||
? state->shortcut->toDisplayString()
|
||||
: QString();
|
||||
recording->setColorOverride(std::nullopt);
|
||||
if (state->manager) {
|
||||
state->manager->stopRecording();
|
||||
}
|
||||
};
|
||||
const auto startRecording = [=] {
|
||||
if (!ensureManager()) {
|
||||
state->pushToTalkToggles.fire(false);
|
||||
pushToTalkWrap->hide(anim::type::instant);
|
||||
return;
|
||||
}
|
||||
state->recording = true;
|
||||
state->recordText = tr::lng_group_call_ptt_recording();
|
||||
recording->setColorOverride(
|
||||
st::groupCallSettingsAttentionButton.textFg->c);
|
||||
auto progress = crl::guard(box, [=](GlobalShortcut shortcut) {
|
||||
state->shortcutText = shortcut->toDisplayString();
|
||||
});
|
||||
auto done = crl::guard(box, [=](GlobalShortcut shortcut) {
|
||||
state->shortcut = shortcut;
|
||||
Core::App().settings().setGroupCallPushToTalkShortcut(shortcut
|
||||
? shortcut->serialize()
|
||||
: QByteArray());
|
||||
applyAndSave();
|
||||
stopRecording();
|
||||
});
|
||||
state->manager->startRecording(std::move(progress), std::move(done));
|
||||
};
|
||||
recording->addClickHandler([=] {
|
||||
if (state->recording) {
|
||||
stopRecording();
|
||||
} else {
|
||||
startRecording();
|
||||
}
|
||||
}), &st::groupCallCheckbox, &st::groupCallRadio));
|
||||
});
|
||||
|
||||
const auto label = pushToTalkInner->add(
|
||||
object_ptr<Ui::LabelSimple>(
|
||||
state->micTestLevel = box->addRow(
|
||||
object_ptr<Ui::LevelMeter>(
|
||||
box.get(),
|
||||
st::groupCallLevelMeter),
|
||||
st::settingsLevelMeterPadding);
|
||||
state->micTestLevel->resize(QSize(0, st::defaultLevelMeter.height));
|
||||
|
||||
state->levelUpdateTimer.setCallback([=] {
|
||||
const auto was = state->micLevel;
|
||||
state->micLevel = state->micTester->getAndResetLevel();
|
||||
state->micLevelAnimation.start([=] {
|
||||
state->micTestLevel->setValue(
|
||||
state->micLevelAnimation.value(state->micLevel));
|
||||
}, was, state->micLevel, kMicTestAnimationDuration);
|
||||
});
|
||||
|
||||
AddSkip(layout);
|
||||
//AddDivider(layout);
|
||||
//AddSkip(layout);
|
||||
|
||||
AddButton(
|
||||
layout,
|
||||
tr::lng_group_call_noise_suppression(),
|
||||
st::groupCallSettingsButton
|
||||
)->toggleOn(rpl::single(
|
||||
settings.groupCallNoiseSuppression()
|
||||
))->toggledChanges(
|
||||
) | rpl::start_with_next([=](bool enabled) {
|
||||
Core::App().settings().setGroupCallNoiseSuppression(enabled);
|
||||
call->setNoiseSuppression(enabled);
|
||||
Core::App().saveSettingsDelayed();
|
||||
}, layout->lifetime());
|
||||
|
||||
using GlobalShortcut = base::GlobalShortcut;
|
||||
struct PushToTalkState {
|
||||
rpl::variable<QString> recordText = tr::lng_group_call_ptt_shortcut();
|
||||
rpl::variable<QString> shortcutText;
|
||||
rpl::event_stream<bool> pushToTalkToggles;
|
||||
std::shared_ptr<base::GlobalShortcutManager> manager;
|
||||
GlobalShortcut shortcut;
|
||||
crl::time delay = 0;
|
||||
bool recording = false;
|
||||
};
|
||||
if (base::GlobalShortcutsAvailable()) {
|
||||
const auto state = box->lifetime().make_state<PushToTalkState>();
|
||||
if (!base::GlobalShortcutsAllowed()) {
|
||||
Core::App().settings().setGroupCallPushToTalk(false);
|
||||
}
|
||||
const auto tryFillFromManager = [=] {
|
||||
state->shortcut = state->manager
|
||||
? state->manager->shortcutFromSerialized(
|
||||
Core::App().settings().groupCallPushToTalkShortcut())
|
||||
: nullptr;
|
||||
state->shortcutText = state->shortcut
|
||||
? state->shortcut->toDisplayString()
|
||||
: QString();
|
||||
};
|
||||
state->manager = settings.groupCallPushToTalk()
|
||||
? call->ensureGlobalShortcutManager()
|
||||
: nullptr;
|
||||
tryFillFromManager();
|
||||
|
||||
state->delay = settings.groupCallPushToTalkDelay();
|
||||
const auto pushToTalk = AddButton(
|
||||
layout,
|
||||
tr::lng_group_call_push_to_talk(),
|
||||
st::groupCallSettingsButton
|
||||
)->toggleOn(rpl::single(
|
||||
settings.groupCallPushToTalk()
|
||||
) | rpl::then(state->pushToTalkToggles.events()));
|
||||
const auto pushToTalkWrap = layout->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
layout,
|
||||
object_ptr<Ui::VerticalLayout>(layout)));
|
||||
const auto pushToTalkInner = pushToTalkWrap->entity();
|
||||
const auto recording = AddButton(
|
||||
pushToTalkInner,
|
||||
st::groupCallDelayLabel),
|
||||
st::groupCallDelayLabelMargin);
|
||||
const auto value = std::clamp(
|
||||
state->delay,
|
||||
crl::time(0),
|
||||
DelayByIndex(kDelaysCount - 1));
|
||||
const auto callback = [=](crl::time delay) {
|
||||
state->delay = delay;
|
||||
label->setText(tr::lng_group_call_ptt_delay(
|
||||
tr::now,
|
||||
lt_delay,
|
||||
FormatDelay(delay)));
|
||||
if (Core::App().settings().groupCallPushToTalkDelay() != delay) {
|
||||
Core::App().settings().setGroupCallPushToTalkDelay(delay);
|
||||
state->recordText.value(),
|
||||
st::groupCallSettingsButton);
|
||||
CreateRightLabel(
|
||||
recording,
|
||||
state->shortcutText.value(),
|
||||
st::groupCallSettingsButton,
|
||||
state->recordText.value());
|
||||
|
||||
const auto applyAndSave = [=] {
|
||||
call->applyGlobalShortcutChanges();
|
||||
Core::App().saveSettingsDelayed();
|
||||
};
|
||||
const auto showPrivacyRequest = [=] {
|
||||
#ifdef Q_OS_MAC
|
||||
if (!Platform::IsMac10_14OrGreater()) {
|
||||
return;
|
||||
}
|
||||
const auto requestInputMonitoring = Platform::IsMac10_15OrGreater();
|
||||
box->getDelegate()->show(Box([=](not_null<Ui::GenericBox*> box) {
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box.get(),
|
||||
rpl::combine(
|
||||
tr::lng_group_call_mac_access(),
|
||||
(requestInputMonitoring
|
||||
? tr::lng_group_call_mac_input()
|
||||
: tr::lng_group_call_mac_accessibility())
|
||||
) | rpl::map([](QString a, QString b) {
|
||||
auto result = Ui::Text::RichLangValue(a);
|
||||
result.append("\n\n").append(Ui::Text::RichLangValue(b));
|
||||
return result;
|
||||
}),
|
||||
st::groupCallBoxLabel),
|
||||
style::margins(
|
||||
st::boxRowPadding.left(),
|
||||
st::boxPadding.top(),
|
||||
st::boxRowPadding.right(),
|
||||
st::boxPadding.bottom()));
|
||||
box->addButton(tr::lng_group_call_mac_settings(), [=] {
|
||||
if (requestInputMonitoring) {
|
||||
Platform::OpenInputMonitoringPrivacySettings();
|
||||
} else {
|
||||
Platform::OpenAccessibilityPrivacySettings();
|
||||
}
|
||||
});
|
||||
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||
|
||||
if (!requestInputMonitoring) {
|
||||
// Accessibility is enabled without app restart, so short-poll it.
|
||||
base::timer_each(
|
||||
kCheckAccessibilityInterval
|
||||
) | rpl::filter([] {
|
||||
return base::GlobalShortcutsAllowed();
|
||||
}) | rpl::start_with_next([=] {
|
||||
box->closeBox();
|
||||
}, box->lifetime());
|
||||
}
|
||||
}));
|
||||
#endif // Q_OS_MAC
|
||||
};
|
||||
const auto ensureManager = [=] {
|
||||
if (state->manager) {
|
||||
return true;
|
||||
} else if (base::GlobalShortcutsAllowed()) {
|
||||
state->manager = call->ensureGlobalShortcutManager();
|
||||
tryFillFromManager();
|
||||
return true;
|
||||
}
|
||||
showPrivacyRequest();
|
||||
return false;
|
||||
};
|
||||
const auto stopRecording = [=] {
|
||||
state->recording = false;
|
||||
state->recordText = tr::lng_group_call_ptt_shortcut();
|
||||
state->shortcutText = state->shortcut
|
||||
? state->shortcut->toDisplayString()
|
||||
: QString();
|
||||
recording->setColorOverride(std::nullopt);
|
||||
if (state->manager) {
|
||||
state->manager->stopRecording();
|
||||
}
|
||||
};
|
||||
const auto startRecording = [=] {
|
||||
if (!ensureManager()) {
|
||||
state->pushToTalkToggles.fire(false);
|
||||
pushToTalkWrap->hide(anim::type::instant);
|
||||
return;
|
||||
}
|
||||
state->recording = true;
|
||||
state->recordText = tr::lng_group_call_ptt_recording();
|
||||
recording->setColorOverride(
|
||||
st::groupCallSettingsAttentionButton.textFg->c);
|
||||
auto progress = crl::guard(box, [=](GlobalShortcut shortcut) {
|
||||
state->shortcutText = shortcut->toDisplayString();
|
||||
});
|
||||
auto done = crl::guard(box, [=](GlobalShortcut shortcut) {
|
||||
state->shortcut = shortcut;
|
||||
Core::App().settings().setGroupCallPushToTalkShortcut(shortcut
|
||||
? shortcut->serialize()
|
||||
: QByteArray());
|
||||
applyAndSave();
|
||||
stopRecording();
|
||||
});
|
||||
state->manager->startRecording(std::move(progress), std::move(done));
|
||||
};
|
||||
recording->addClickHandler([=] {
|
||||
if (state->recording) {
|
||||
stopRecording();
|
||||
} else {
|
||||
startRecording();
|
||||
}
|
||||
});
|
||||
|
||||
const auto label = pushToTalkInner->add(
|
||||
object_ptr<Ui::LabelSimple>(
|
||||
pushToTalkInner,
|
||||
st::groupCallDelayLabel),
|
||||
st::groupCallDelayLabelMargin);
|
||||
const auto value = std::clamp(
|
||||
state->delay,
|
||||
crl::time(0),
|
||||
DelayByIndex(kDelaysCount - 1));
|
||||
const auto callback = [=](crl::time delay) {
|
||||
state->delay = delay;
|
||||
label->setText(tr::lng_group_call_ptt_delay(
|
||||
tr::now,
|
||||
lt_delay,
|
||||
FormatDelay(delay)));
|
||||
if (Core::App().settings().groupCallPushToTalkDelay() != delay) {
|
||||
Core::App().settings().setGroupCallPushToTalkDelay(delay);
|
||||
applyAndSave();
|
||||
}
|
||||
};
|
||||
callback(value);
|
||||
const auto slider = pushToTalkInner->add(
|
||||
object_ptr<Ui::MediaSlider>(
|
||||
pushToTalkInner,
|
||||
st::groupCallDelaySlider),
|
||||
st::groupCallDelayMargin);
|
||||
slider->resize(st::groupCallDelaySlider.seekSize);
|
||||
slider->setPseudoDiscrete(
|
||||
kDelaysCount,
|
||||
DelayByIndex,
|
||||
value,
|
||||
callback);
|
||||
|
||||
pushToTalkWrap->toggle(
|
||||
settings.groupCallPushToTalk(),
|
||||
anim::type::instant);
|
||||
pushToTalk->toggledChanges(
|
||||
) | rpl::start_with_next([=](bool toggled) {
|
||||
if (!toggled) {
|
||||
stopRecording();
|
||||
} else if (!ensureManager()) {
|
||||
state->pushToTalkToggles.fire(false);
|
||||
pushToTalkWrap->hide(anim::type::instant);
|
||||
return;
|
||||
}
|
||||
Core::App().settings().setGroupCallPushToTalk(toggled);
|
||||
applyAndSave();
|
||||
}
|
||||
};
|
||||
callback(value);
|
||||
const auto slider = pushToTalkInner->add(
|
||||
object_ptr<Ui::MediaSlider>(
|
||||
pushToTalkInner,
|
||||
st::groupCallDelaySlider),
|
||||
st::groupCallDelayMargin);
|
||||
slider->resize(st::groupCallDelaySlider.seekSize);
|
||||
slider->setPseudoDiscrete(
|
||||
kDelaysCount,
|
||||
DelayByIndex,
|
||||
value,
|
||||
callback);
|
||||
pushToTalkWrap->toggle(toggled, anim::type::normal);
|
||||
}, pushToTalk->lifetime());
|
||||
|
||||
pushToTalkWrap->toggle(
|
||||
settings.groupCallPushToTalk(),
|
||||
anim::type::instant);
|
||||
pushToTalk->toggledChanges(
|
||||
) | rpl::start_with_next([=](bool toggled) {
|
||||
if (!toggled) {
|
||||
stopRecording();
|
||||
} else if (!ensureManager()) {
|
||||
state->pushToTalkToggles.fire(false);
|
||||
pushToTalkWrap->hide(anim::type::instant);
|
||||
return;
|
||||
}
|
||||
Core::App().settings().setGroupCallPushToTalk(toggled);
|
||||
applyAndSave();
|
||||
pushToTalkWrap->toggle(toggled, anim::type::normal);
|
||||
}, pushToTalk->lifetime());
|
||||
auto boxKeyFilter = [=](not_null<QEvent*> e) {
|
||||
return (e->type() == QEvent::KeyPress && state->recording)
|
||||
? base::EventFilterResult::Cancel
|
||||
: base::EventFilterResult::Continue;
|
||||
};
|
||||
box->lifetime().make_state<base::unique_qptr<QObject>>(
|
||||
base::install_event_filter(box, std::move(boxKeyFilter)));
|
||||
}
|
||||
|
||||
auto boxKeyFilter = [=](not_null<QEvent*> e) {
|
||||
return (e->type() == QEvent::KeyPress && state->recording)
|
||||
? base::EventFilterResult::Cancel
|
||||
: base::EventFilterResult::Continue;
|
||||
};
|
||||
box->lifetime().make_state<base::unique_qptr<QObject>>(
|
||||
base::install_event_filter(box, std::move(boxKeyFilter)));
|
||||
AddSkip(layout);
|
||||
//AddDivider(layout);
|
||||
//AddSkip(layout);
|
||||
}
|
||||
|
||||
AddSkip(layout);
|
||||
//AddDivider(layout);
|
||||
//AddSkip(layout);
|
||||
|
||||
auto shareLink = Fn<void()>();
|
||||
if (peer->isChannel()
|
||||
&& peer->asChannel()->hasUsername()
|
||||
|
@ -644,15 +645,17 @@ void SettingsBox(
|
|||
});
|
||||
}
|
||||
|
||||
box->setShowFinishedCallback([=] {
|
||||
// Means we finished showing the box.
|
||||
crl::on_main(box, [=] {
|
||||
state->micTester = std::make_unique<Webrtc::AudioInputTester>(
|
||||
Core::App().settings().callAudioBackend(),
|
||||
Core::App().settings().callInputDeviceId());
|
||||
state->levelUpdateTimer.callEach(kMicTestUpdateInterval);
|
||||
if (!rtmp) {
|
||||
box->setShowFinishedCallback([=] {
|
||||
// Means we finished showing the box.
|
||||
crl::on_main(box, [=] {
|
||||
state->micTester = std::make_unique<Webrtc::AudioInputTester>(
|
||||
Core::App().settings().callAudioBackend(),
|
||||
Core::App().settings().callInputDeviceId());
|
||||
state->levelUpdateTimer.callEach(kMicTestUpdateInterval);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
box->setTitle(tr::lng_group_call_settings_title());
|
||||
box->boxClosing(
|
||||
|
|
|
@ -876,6 +876,9 @@ rpl::producer<QString> MuteButtonTooltip(not_null<GroupCall*> call) {
|
|||
// : tr::lng_group_call_set_reminder();
|
||||
// }) | rpl::flatten_latest();
|
||||
// }
|
||||
if (call->rtmp()) {
|
||||
return nullptr;
|
||||
}
|
||||
return call->mutedValue(
|
||||
) | rpl::map([](MuteState muted) {
|
||||
switch (muted) {
|
||||
|
|
|
@ -492,7 +492,7 @@ void GroupCall::processQueuedUpdates() {
|
|||
}
|
||||
|
||||
void GroupCall::computeParticipantsCount() {
|
||||
_fullCount = _allParticipantsLoaded
|
||||
_fullCount = (_allParticipantsLoaded && !_listenersHidden)
|
||||
? int(_participants.size())
|
||||
: std::max(int(_participants.size()), _serverParticipantsCount);
|
||||
}
|
||||
|
|
|
@ -825,7 +825,17 @@ void CallMuteButton::init() {
|
|||
) | rpl::start_with_next([=](QRect clip) {
|
||||
Painter p(_content);
|
||||
|
||||
_icons[_iconState.index]->paint(p, _muteIconRect.x(), _muteIconRect.y());
|
||||
const auto expand = _state.current().expandType;
|
||||
if (expand == CallMuteButtonExpandType::Expanded) {
|
||||
st::callMuteFromFullScreen.paintInCenter(p, _muteIconRect);
|
||||
} else if (expand == CallMuteButtonExpandType::Normal) {
|
||||
st::callMuteToFullScreen.paintInCenter(p, _muteIconRect);
|
||||
} else {
|
||||
_icons[_iconState.index]->paint(
|
||||
p,
|
||||
_muteIconRect.x(),
|
||||
_muteIconRect.y());
|
||||
}
|
||||
|
||||
if (_radialInfo.state.has_value() && _switchAnimation.animating()) {
|
||||
const auto radialProgress = _radialInfo.realShowProgress;
|
||||
|
|
|
@ -43,11 +43,18 @@ enum class CallMuteButtonType {
|
|||
ScheduledNotify,
|
||||
};
|
||||
|
||||
enum class CallMuteButtonExpandType {
|
||||
None,
|
||||
Normal,
|
||||
Expanded,
|
||||
};
|
||||
|
||||
struct CallMuteButtonState {
|
||||
QString text;
|
||||
QString subtext;
|
||||
QString tooltip;
|
||||
CallMuteButtonType type = CallMuteButtonType::Connecting;
|
||||
CallMuteButtonExpandType expandType = CallMuteButtonExpandType::None;
|
||||
};
|
||||
|
||||
class CallMuteButton final : private AbstractTooltipShower {
|
||||
|
|
Loading…
Add table
Reference in a new issue