Add device selectors to one-on-one calls.
Before Width: | Height: | Size: 214 B After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 426 B After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 912 B After Width: | Height: | Size: 1.6 KiB |
BIN
Telegram/Resources/icons/calls/mini_calls_arrow.png
Normal file
After Width: | Height: | Size: 240 B |
BIN
Telegram/Resources/icons/calls/mini_calls_arrow@2x.png
Normal file
After Width: | Height: | Size: 324 B |
BIN
Telegram/Resources/icons/calls/mini_calls_arrow@3x.png
Normal file
After Width: | Height: | Size: 422 B |
@ -155,6 +155,8 @@ callMicrophoneMute: CallButton(callAnswer) {
|
|||||||
bg: callIconBg;
|
bg: callIconBg;
|
||||||
outerBg: callMuteRipple;
|
outerBg: callMuteRipple;
|
||||||
label: callButtonLabel;
|
label: callButtonLabel;
|
||||||
|
cornerButtonPosition: point(40px, 4px);
|
||||||
|
cornerButtonBorder: 2px;
|
||||||
}
|
}
|
||||||
callMicrophoneUnmute: CallButton(callMicrophoneMute) {
|
callMicrophoneUnmute: CallButton(callMicrophoneMute) {
|
||||||
button: IconButton(callButton) {
|
button: IconButton(callButton) {
|
||||||
@ -181,6 +183,34 @@ callCameraUnmute: CallButton(callMicrophoneUnmute) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
callCornerButtonInner: IconButton {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
|
||||||
|
iconPosition: point(-1px, -1px);
|
||||||
|
|
||||||
|
rippleAreaPosition: point(0px, 0px);
|
||||||
|
rippleAreaSize: 20px;
|
||||||
|
ripple: defaultRippleAnimation;
|
||||||
|
}
|
||||||
|
callCornerButton: CallButton(callMicrophoneMute) {
|
||||||
|
button: IconButton(callCornerButtonInner) {
|
||||||
|
icon: icon {{ "calls/mini_calls_arrow", callIconFg }};
|
||||||
|
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||||
|
color: callMuteRipple;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bgSize: 20px;
|
||||||
|
bgPosition: point(0px, 0px);
|
||||||
|
}
|
||||||
|
callCornerButtonInactive: CallButton(callMicrophoneUnmute, callCornerButton) {
|
||||||
|
button: IconButton(callCornerButtonInner) {
|
||||||
|
icon: icon {{ "calls/mini_calls_arrow", callIconFgActive }};
|
||||||
|
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||||
|
color: callIconActiveRipple;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
callScreencastOn: CallButton(callMicrophoneMute) {
|
callScreencastOn: CallButton(callMicrophoneMute) {
|
||||||
button: IconButton(callButton) {
|
button: IconButton(callButton) {
|
||||||
icon: icon {{ "calls/calls_present", callIconFg }};
|
icon: icon {{ "calls/calls_present", callIconFg }};
|
||||||
@ -576,6 +606,18 @@ groupCallMenuAbout: FlatLabel(defaultFlatLabel) {
|
|||||||
minWidth: 200px;
|
minWidth: 200px;
|
||||||
maxHeight: 92px;
|
maxHeight: 92px;
|
||||||
}
|
}
|
||||||
|
callDeviceSelectionLabel: FlatLabel(defaultSubsectionTitle) {
|
||||||
|
textFg: groupCallActiveFg;
|
||||||
|
minWidth: 200px;
|
||||||
|
maxHeight: 20px;
|
||||||
|
}
|
||||||
|
callDeviceSelectionMenu: PopupMenu(groupCallPopupMenu) {
|
||||||
|
scrollPadding: margins(0px, 3px, 0px, 8px);
|
||||||
|
menu: Menu(groupCallMenu) {
|
||||||
|
widthMin: 240px;
|
||||||
|
itemPadding: margins(17px, 8px, 17px, 7px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
groupCallRecordingTimerPadding: margins(0px, 4px, 0px, 4px);
|
groupCallRecordingTimerPadding: margins(0px, 4px, 0px, 4px);
|
||||||
groupCallRecordingTimerFont: font(12px);
|
groupCallRecordingTimerFont: font(12px);
|
||||||
|
@ -1310,6 +1310,19 @@ void Call::toggleScreenSharing(std::optional<QString> uniqueId) {
|
|||||||
_videoOutgoing->setState(Webrtc::VideoState::Active);
|
_videoOutgoing->setState(Webrtc::VideoState::Active);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Call::playbackDeviceIdValue() const
|
||||||
|
-> rpl::producer<Webrtc::DeviceResolvedId> {
|
||||||
|
return _playbackDeviceId.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<Webrtc::DeviceResolvedId> Call::captureDeviceIdValue() const {
|
||||||
|
return _captureDeviceId.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<Webrtc::DeviceResolvedId> Call::cameraDeviceIdValue() const {
|
||||||
|
return _cameraDeviceId.value();
|
||||||
|
}
|
||||||
|
|
||||||
void Call::finish(FinishType type, const MTPPhoneCallDiscardReason &reason) {
|
void Call::finish(FinishType type, const MTPPhoneCallDiscardReason &reason) {
|
||||||
Expects(type != FinishType::None);
|
Expects(type != FinishType::None);
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ enum class AudioState;
|
|||||||
namespace Webrtc {
|
namespace Webrtc {
|
||||||
enum class VideoState;
|
enum class VideoState;
|
||||||
class VideoTrack;
|
class VideoTrack;
|
||||||
|
struct DeviceResolvedId;
|
||||||
} // namespace Webrtc
|
} // namespace Webrtc
|
||||||
|
|
||||||
namespace Calls {
|
namespace Calls {
|
||||||
@ -220,6 +221,13 @@ public:
|
|||||||
void toggleCameraSharing(bool enabled);
|
void toggleCameraSharing(bool enabled);
|
||||||
void toggleScreenSharing(std::optional<QString> uniqueId);
|
void toggleScreenSharing(std::optional<QString> uniqueId);
|
||||||
|
|
||||||
|
[[nodiscard]] auto playbackDeviceIdValue() const
|
||||||
|
-> rpl::producer<Webrtc::DeviceResolvedId>;
|
||||||
|
[[nodiscard]] auto captureDeviceIdValue() const
|
||||||
|
-> rpl::producer<Webrtc::DeviceResolvedId>;
|
||||||
|
[[nodiscard]] auto cameraDeviceIdValue() const
|
||||||
|
-> rpl::producer<Webrtc::DeviceResolvedId>;
|
||||||
|
|
||||||
[[nodiscard]] rpl::lifetime &lifetime() {
|
[[nodiscard]] rpl::lifetime &lifetime() {
|
||||||
return _lifetime;
|
return _lifetime;
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||||||
#include "data/data_cloud_file.h"
|
#include "data/data_cloud_file.h"
|
||||||
#include "data/data_changes.h"
|
#include "data/data_changes.h"
|
||||||
#include "calls/group/calls_group_common.h"
|
#include "calls/group/calls_group_common.h"
|
||||||
|
#include "calls/ui/calls_device_menu.h"
|
||||||
#include "calls/calls_emoji_fingerprint.h"
|
#include "calls/calls_emoji_fingerprint.h"
|
||||||
#include "calls/calls_signal_bars.h"
|
#include "calls/calls_signal_bars.h"
|
||||||
#include "calls/calls_userpic.h"
|
#include "calls/calls_userpic.h"
|
||||||
@ -24,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||||||
#include "ui/widgets/call_button.h"
|
#include "ui/widgets/call_button.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
|
#include "ui/widgets/popup_menu.h"
|
||||||
#include "ui/widgets/shadow.h"
|
#include "ui/widgets/shadow.h"
|
||||||
#include "ui/widgets/rp_window.h"
|
#include "ui/widgets/rp_window.h"
|
||||||
#include "ui/layers/layer_manager.h"
|
#include "ui/layers/layer_manager.h"
|
||||||
@ -130,6 +132,7 @@ Panel::Panel(not_null<Call*> call)
|
|||||||
initWidget();
|
initWidget();
|
||||||
initControls();
|
initControls();
|
||||||
initLayout();
|
initLayout();
|
||||||
|
initMediaDeviceToggles();
|
||||||
showAndActivate();
|
showAndActivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -736,6 +739,58 @@ void Panel::initGeometry() {
|
|||||||
updateControlsGeometry();
|
updateControlsGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Panel::initMediaDeviceToggles() {
|
||||||
|
_cameraDeviceToggle = _camera->addCornerButton(
|
||||||
|
st::callCornerButton,
|
||||||
|
&st::callCornerButtonInactive);
|
||||||
|
_audioDeviceToggle = _mute->entity()->addCornerButton(
|
||||||
|
st::callCornerButton,
|
||||||
|
&st::callCornerButtonInactive);
|
||||||
|
|
||||||
|
_cameraDeviceToggle->setClickedCallback([=] {
|
||||||
|
showDevicesMenu(_cameraDeviceToggle, {
|
||||||
|
{ Webrtc::DeviceType::Camera, _call->cameraDeviceIdValue() },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
_audioDeviceToggle->setClickedCallback([=] {
|
||||||
|
showDevicesMenu(_audioDeviceToggle, {
|
||||||
|
{ Webrtc::DeviceType::Playback, _call->playbackDeviceIdValue() },
|
||||||
|
{ Webrtc::DeviceType::Capture, _call->captureDeviceIdValue() },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel::showDevicesMenu(
|
||||||
|
not_null<QWidget*> button,
|
||||||
|
std::vector<DeviceSelection> types) {
|
||||||
|
if (!_call || _devicesMenu) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto chosen = [=](Webrtc::DeviceType type, QString id) {
|
||||||
|
switch (type) {
|
||||||
|
case Webrtc::DeviceType::Playback:
|
||||||
|
Core::App().settings().setCallPlaybackDeviceId(id);
|
||||||
|
break;
|
||||||
|
case Webrtc::DeviceType::Capture:
|
||||||
|
Core::App().settings().setCallCaptureDeviceId(id);
|
||||||
|
break;
|
||||||
|
case Webrtc::DeviceType::Camera:
|
||||||
|
Core::App().settings().setCameraDeviceId(id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Core::App().saveSettingsDelayed();
|
||||||
|
};
|
||||||
|
_devicesMenu = MakeDeviceSelectionMenu(
|
||||||
|
widget(),
|
||||||
|
&Core::App().mediaDevices(),
|
||||||
|
std::move(types),
|
||||||
|
chosen);
|
||||||
|
_devicesMenu->setForcedVerticalOrigin(
|
||||||
|
Ui::PopupMenu::VerticalOrigin::Bottom);
|
||||||
|
_devicesMenu->popup(button->mapToGlobal(QPoint())
|
||||||
|
- QPoint(st::callDeviceSelectionMenu.menu.widthMin / 2, 0));
|
||||||
|
}
|
||||||
|
|
||||||
void Panel::refreshOutgoingPreviewInBody(State state) {
|
void Panel::refreshOutgoingPreviewInBody(State state) {
|
||||||
const auto inBody = (state != State::Established)
|
const auto inBody = (state != State::Established)
|
||||||
&& (_call->videoOutgoing()->state() != Webrtc::VideoState::Inactive)
|
&& (_call->videoOutgoing()->state() != Webrtc::VideoState::Inactive)
|
||||||
|
@ -37,6 +37,7 @@ class FadeWrap;
|
|||||||
template <typename Widget>
|
template <typename Widget>
|
||||||
class PaddingWrap;
|
class PaddingWrap;
|
||||||
class RpWindow;
|
class RpWindow;
|
||||||
|
class PopupMenu;
|
||||||
namespace GL {
|
namespace GL {
|
||||||
enum class Backend;
|
enum class Backend;
|
||||||
} // namespace GL
|
} // namespace GL
|
||||||
@ -55,6 +56,7 @@ namespace Calls {
|
|||||||
class Userpic;
|
class Userpic;
|
||||||
class SignalBars;
|
class SignalBars;
|
||||||
class VideoBubble;
|
class VideoBubble;
|
||||||
|
struct DeviceSelection;
|
||||||
|
|
||||||
class Panel final : private Group::Ui::DesktopCapture::ChooseSourceDelegate {
|
class Panel final : private Group::Ui::DesktopCapture::ChooseSourceDelegate {
|
||||||
public:
|
public:
|
||||||
@ -104,6 +106,7 @@ private:
|
|||||||
void initControls();
|
void initControls();
|
||||||
void reinitWithCall(Call *call);
|
void reinitWithCall(Call *call);
|
||||||
void initLayout();
|
void initLayout();
|
||||||
|
void initMediaDeviceToggles();
|
||||||
void initGeometry();
|
void initGeometry();
|
||||||
|
|
||||||
[[nodiscard]] bool handleClose() const;
|
[[nodiscard]] bool handleClose() const;
|
||||||
@ -126,6 +129,10 @@ private:
|
|||||||
void showRemoteLowBattery();
|
void showRemoteLowBattery();
|
||||||
void refreshAnswerHangupRedialLabel();
|
void refreshAnswerHangupRedialLabel();
|
||||||
|
|
||||||
|
void showDevicesMenu(
|
||||||
|
not_null<QWidget*> button,
|
||||||
|
std::vector<DeviceSelection> types);
|
||||||
|
|
||||||
[[nodiscard]] QRect incomingFrameGeometry() const;
|
[[nodiscard]] QRect incomingFrameGeometry() const;
|
||||||
[[nodiscard]] QRect outgoingFrameGeometry() const;
|
[[nodiscard]] QRect outgoingFrameGeometry() const;
|
||||||
|
|
||||||
@ -156,8 +163,10 @@ private:
|
|||||||
Ui::Animations::Simple _hangupShownProgress;
|
Ui::Animations::Simple _hangupShownProgress;
|
||||||
object_ptr<Ui::FadeWrap<Ui::CallButton>> _screencast;
|
object_ptr<Ui::FadeWrap<Ui::CallButton>> _screencast;
|
||||||
object_ptr<Ui::CallButton> _camera;
|
object_ptr<Ui::CallButton> _camera;
|
||||||
|
Ui::CallButton *_cameraDeviceToggle = nullptr;
|
||||||
base::unique_qptr<Ui::CallButton> _startVideo;
|
base::unique_qptr<Ui::CallButton> _startVideo;
|
||||||
object_ptr<Ui::FadeWrap<Ui::CallButton>> _mute;
|
object_ptr<Ui::FadeWrap<Ui::CallButton>> _mute;
|
||||||
|
Ui::CallButton *_audioDeviceToggle = nullptr;
|
||||||
object_ptr<Ui::FlatLabel> _name;
|
object_ptr<Ui::FlatLabel> _name;
|
||||||
object_ptr<Ui::FlatLabel> _status;
|
object_ptr<Ui::FlatLabel> _status;
|
||||||
object_ptr<Ui::RpWidget> _fingerprint = { nullptr };
|
object_ptr<Ui::RpWidget> _fingerprint = { nullptr };
|
||||||
@ -170,6 +179,8 @@ private:
|
|||||||
int _bodyTop = 0;
|
int _bodyTop = 0;
|
||||||
int _buttonsTop = 0;
|
int _buttonsTop = 0;
|
||||||
|
|
||||||
|
base::unique_qptr<Ui::PopupMenu> _devicesMenu;
|
||||||
|
|
||||||
base::Timer _updateDurationTimer;
|
base::Timer _updateDurationTimer;
|
||||||
base::Timer _updateOuterRippleTimer;
|
base::Timer _updateOuterRippleTimer;
|
||||||
|
|
||||||
|
250
Telegram/SourceFiles/calls/ui/calls_device_menu.cpp
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
/*
|
||||||
|
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/ui/calls_device_menu.h"
|
||||||
|
|
||||||
|
#include "lang/lang_keys.h"
|
||||||
|
#include "ui/widgets/menu/menu_item_base.h"
|
||||||
|
#include "ui/widgets/checkbox.h"
|
||||||
|
#include "ui/widgets/labels.h"
|
||||||
|
#include "ui/widgets/popup_menu.h"
|
||||||
|
#include "ui/widgets/scroll_area.h"
|
||||||
|
#include "ui/wrap/vertical_layout.h"
|
||||||
|
#include "webrtc/webrtc_device_common.h"
|
||||||
|
#include "webrtc/webrtc_environment.h"
|
||||||
|
#include "styles/style_calls.h"
|
||||||
|
#include "styles/style_layers.h"
|
||||||
|
|
||||||
|
namespace Calls {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class Subsection final : public Ui::Menu::ItemBase {
|
||||||
|
public:
|
||||||
|
Subsection(
|
||||||
|
not_null<RpWidget*> parent,
|
||||||
|
const style::Menu &st,
|
||||||
|
const QString &text);
|
||||||
|
|
||||||
|
not_null<QAction*> action() const override;
|
||||||
|
bool isEnabled() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int contentHeight() const override;
|
||||||
|
|
||||||
|
const style::Menu &_st;
|
||||||
|
const base::unique_qptr<Ui::FlatLabel> _text;
|
||||||
|
const not_null<QAction*> _dummyAction;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class Selector final : public Ui::Menu::ItemBase {
|
||||||
|
public:
|
||||||
|
Selector(
|
||||||
|
not_null<RpWidget*> parent,
|
||||||
|
const style::Menu &st,
|
||||||
|
rpl::producer<std::vector<Webrtc::DeviceInfo>> devices,
|
||||||
|
rpl::producer<Webrtc::DeviceResolvedId> chosen,
|
||||||
|
Fn<void(QString)> selected);
|
||||||
|
|
||||||
|
not_null<QAction*> action() const override;
|
||||||
|
bool isEnabled() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int contentHeight() const override;
|
||||||
|
[[nodiscard]] int registerId(const QString &id);
|
||||||
|
|
||||||
|
const base::unique_qptr<Ui::ScrollArea> _scroll;
|
||||||
|
const not_null<Ui::VerticalLayout*> _list;
|
||||||
|
const not_null<QAction*> _dummyAction;
|
||||||
|
|
||||||
|
base::flat_map<QString, int> _ids;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
Subsection::Subsection(
|
||||||
|
not_null<RpWidget*> parent,
|
||||||
|
const style::Menu &st,
|
||||||
|
const QString &text)
|
||||||
|
: Ui::Menu::ItemBase(parent, st)
|
||||||
|
, _st(st)
|
||||||
|
, _text(base::make_unique_q<Ui::FlatLabel>(
|
||||||
|
this,
|
||||||
|
text,
|
||||||
|
st::callDeviceSelectionLabel))
|
||||||
|
, _dummyAction(new QAction(parent)) {
|
||||||
|
setPointerCursor(false);
|
||||||
|
|
||||||
|
initResizeHook(parent->sizeValue());
|
||||||
|
|
||||||
|
_text->resizeToWidth(st::callDeviceSelectionLabel.minWidth);
|
||||||
|
_text->moveToLeft(st.itemPadding.left(), st.itemPadding.top());
|
||||||
|
}
|
||||||
|
|
||||||
|
not_null<QAction*> Subsection::action() const {
|
||||||
|
return _dummyAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Subsection::isEnabled() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Subsection::contentHeight() const {
|
||||||
|
return _st.itemPadding.top()
|
||||||
|
+ _text->height()
|
||||||
|
+ _st.itemPadding.bottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
Selector::Selector(
|
||||||
|
not_null<RpWidget*> parent,
|
||||||
|
const style::Menu &st,
|
||||||
|
rpl::producer<std::vector<Webrtc::DeviceInfo>> devices,
|
||||||
|
rpl::producer<Webrtc::DeviceResolvedId> chosen,
|
||||||
|
Fn<void(QString)> selected)
|
||||||
|
: Ui::Menu::ItemBase(parent, st)
|
||||||
|
, _scroll(base::make_unique_q<Ui::ScrollArea>(this))
|
||||||
|
, _list(_scroll->setOwnedWidget(object_ptr<Ui::VerticalLayout>(this)))
|
||||||
|
, _dummyAction(new QAction(parent)) {
|
||||||
|
setPointerCursor(false);
|
||||||
|
|
||||||
|
initResizeHook(parent->sizeValue());
|
||||||
|
|
||||||
|
const auto padding = st.itemPadding;
|
||||||
|
const auto group = std::make_shared<Ui::RadiobuttonGroup>();
|
||||||
|
std::move(
|
||||||
|
chosen
|
||||||
|
) | rpl::start_with_next([=](Webrtc::DeviceResolvedId id) {
|
||||||
|
const auto value = id.isDefault() ? 0 : registerId(id.value);
|
||||||
|
if (!group->hasValue() || group->current() != value) {
|
||||||
|
group->setValue(value);
|
||||||
|
}
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
|
group->setChangedCallback([=](int value) {
|
||||||
|
if (value == 0) {
|
||||||
|
selected({});
|
||||||
|
} else {
|
||||||
|
for (const auto &[id, index] : _ids) {
|
||||||
|
if (index == value) {
|
||||||
|
selected(id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
std::move(
|
||||||
|
devices
|
||||||
|
) | rpl::start_with_next([=](const std::vector<Webrtc::DeviceInfo> &v) {
|
||||||
|
while (_list->count()) {
|
||||||
|
delete _list->widgetAt(0);
|
||||||
|
}
|
||||||
|
_list->add(
|
||||||
|
object_ptr<Ui::Radiobutton>(
|
||||||
|
_list.get(),
|
||||||
|
group,
|
||||||
|
0,
|
||||||
|
tr::lng_settings_call_device_default(tr::now),
|
||||||
|
st::groupCallCheckbox,
|
||||||
|
st::groupCallRadio),
|
||||||
|
padding);
|
||||||
|
for (const auto &device : v) {
|
||||||
|
if (device.inactive) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_list->add(
|
||||||
|
object_ptr<Ui::Radiobutton>(
|
||||||
|
_list.get(),
|
||||||
|
group,
|
||||||
|
registerId(device.id),
|
||||||
|
device.name,
|
||||||
|
st::groupCallCheckbox,
|
||||||
|
st::groupCallRadio),
|
||||||
|
padding);
|
||||||
|
}
|
||||||
|
resize(width(), contentHeight());
|
||||||
|
}, lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
not_null<QAction*> Selector::action() const {
|
||||||
|
return _dummyAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Selector::isEnabled() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Selector::contentHeight() const {
|
||||||
|
_list->resizeToWidth(width());
|
||||||
|
if (_list->count() <= 3) {
|
||||||
|
_scroll->resize(width(), _list->height());
|
||||||
|
} else {
|
||||||
|
_scroll->resize(
|
||||||
|
width(),
|
||||||
|
3.5 * st::defaultRadio.diameter);
|
||||||
|
}
|
||||||
|
return _scroll->height();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Selector::registerId(const QString &id) {
|
||||||
|
auto &result = _ids[id];
|
||||||
|
if (!result) {
|
||||||
|
result = int(_ids.size());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddDeviceSelection(
|
||||||
|
not_null<Ui::PopupMenu*> menu,
|
||||||
|
not_null<Webrtc::Environment*> environment,
|
||||||
|
DeviceSelection type,
|
||||||
|
Fn<void(QString)> selected) {
|
||||||
|
const auto title = [&] {
|
||||||
|
switch (type.type) {
|
||||||
|
case Webrtc::DeviceType::Camera:
|
||||||
|
return tr::lng_settings_call_camera(tr::now);
|
||||||
|
case Webrtc::DeviceType::Playback:
|
||||||
|
return tr::lng_settings_call_section_output(tr::now);
|
||||||
|
case Webrtc::DeviceType::Capture:
|
||||||
|
return tr::lng_settings_call_section_input(tr::now);
|
||||||
|
}
|
||||||
|
Unexpected("Type in AddDeviceSelection.");
|
||||||
|
}();
|
||||||
|
menu->addAction(
|
||||||
|
base::make_unique_q<Subsection>(menu, menu->st().menu, title));
|
||||||
|
menu->addAction(
|
||||||
|
base::make_unique_q<Selector>(
|
||||||
|
menu,
|
||||||
|
menu->st().menu,
|
||||||
|
environment->devicesValue(type.type),
|
||||||
|
std::move(type.chosen),
|
||||||
|
selected));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
base::unique_qptr<Ui::PopupMenu> MakeDeviceSelectionMenu(
|
||||||
|
not_null<Ui::RpWidget*> parent,
|
||||||
|
not_null<Webrtc::Environment*> environment,
|
||||||
|
std::vector<DeviceSelection> types,
|
||||||
|
Fn<void(Webrtc::DeviceType, QString)> choose) {
|
||||||
|
auto result = base::make_unique_q<Ui::PopupMenu>(
|
||||||
|
parent,
|
||||||
|
st::callDeviceSelectionMenu);
|
||||||
|
const auto raw = result.get();
|
||||||
|
for (auto type : types) {
|
||||||
|
if (!raw->empty()) {
|
||||||
|
raw->addSeparator();
|
||||||
|
}
|
||||||
|
const auto selected = [=, type = type.type](QString id) {
|
||||||
|
choose(type, id);
|
||||||
|
};
|
||||||
|
AddDeviceSelection(raw, environment, std::move(type), selected);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Calls
|
36
Telegram/SourceFiles/calls/ui/calls_device_menu.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
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 "base/unique_qptr.h"
|
||||||
|
|
||||||
|
namespace Webrtc {
|
||||||
|
class Environment;
|
||||||
|
struct DeviceResolvedId;
|
||||||
|
enum class DeviceType : uchar;
|
||||||
|
} // namespace Webrtc
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class RpWidget;
|
||||||
|
class PopupMenu;
|
||||||
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace Calls {
|
||||||
|
|
||||||
|
struct DeviceSelection {
|
||||||
|
Webrtc::DeviceType type;
|
||||||
|
rpl::producer<Webrtc::DeviceResolvedId> chosen;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] base::unique_qptr<Ui::PopupMenu> MakeDeviceSelectionMenu(
|
||||||
|
not_null<Ui::RpWidget*> parent,
|
||||||
|
not_null<Webrtc::Environment*> environment,
|
||||||
|
std::vector<DeviceSelection> types,
|
||||||
|
Fn<void(Webrtc::DeviceType, QString)> choose);
|
||||||
|
|
||||||
|
} // namespace Calls
|
@ -64,6 +64,8 @@ PRIVATE
|
|||||||
calls/group/ui/calls_group_scheduled_labels.h
|
calls/group/ui/calls_group_scheduled_labels.h
|
||||||
calls/group/ui/desktop_capture_choose_source.cpp
|
calls/group/ui/desktop_capture_choose_source.cpp
|
||||||
calls/group/ui/desktop_capture_choose_source.h
|
calls/group/ui/desktop_capture_choose_source.h
|
||||||
|
calls/ui/calls_device_menu.cpp
|
||||||
|
calls/ui/calls_device_menu.h
|
||||||
|
|
||||||
chat_helpers/field_characters_count_manager.cpp
|
chat_helpers/field_characters_count_manager.cpp
|
||||||
chat_helpers/field_characters_count_manager.h
|
chat_helpers/field_characters_count_manager.h
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit fc5386f1fd4a17fafa88f1bca544e92c4c0ddf99
|
Subproject commit 47ec1b0455ac1f2faab68a4c859baab7eef9e136
|