/* 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 parent, const style::Menu &st, const QString &text); not_null action() const override; bool isEnabled() const override; private: int contentHeight() const override; const style::Menu &_st; const base::unique_qptr _text; const not_null _dummyAction; }; class Selector final : public Ui::Menu::ItemBase { public: Selector( not_null parent, const style::Menu &st, rpl::producer> devices, rpl::producer chosen, Fn selected); not_null action() const override; bool isEnabled() const override; private: int contentHeight() const override; [[nodiscard]] int registerId(const QString &id); const base::unique_qptr _scroll; const not_null _list; const not_null _dummyAction; base::flat_map _ids; }; Subsection::Subsection( not_null parent, const style::Menu &st, const QString &text) : Ui::Menu::ItemBase(parent, st) , _st(st) , _text(base::make_unique_q( 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 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 parent, const style::Menu &st, rpl::producer> devices, rpl::producer chosen, Fn selected) : Ui::Menu::ItemBase(parent, st) , _scroll(base::make_unique_q(this)) , _list(_scroll->setOwnedWidget(object_ptr(this))) , _dummyAction(new QAction(parent)) { setPointerCursor(false); initResizeHook(parent->sizeValue()); const auto padding = st.itemPadding; const auto group = std::make_shared(); 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 &v) { while (_list->count()) { delete _list->widgetAt(0); } _list->add( object_ptr( _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( _list.get(), group, registerId(device.id), device.name, st::groupCallCheckbox, st::groupCallRadio), padding); } resize(width(), contentHeight()); }, lifetime()); } not_null 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 menu, not_null environment, DeviceSelection type, Fn 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(menu, menu->st().menu, title)); menu->addAction( base::make_unique_q( menu, menu->st().menu, environment->devicesValue(type.type), std::move(type.chosen), selected)); } } // namespace base::unique_qptr MakeDeviceSelectionMenu( not_null parent, not_null environment, std::vector types, Fn choose) { auto result = base::make_unique_q( 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