calls_device_menu.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "calls/ui/calls_device_menu.h"
  8. #include "lang/lang_keys.h"
  9. #include "ui/widgets/menu/menu_item_base.h"
  10. #include "ui/widgets/checkbox.h"
  11. #include "ui/widgets/labels.h"
  12. #include "ui/widgets/popup_menu.h"
  13. #include "ui/widgets/scroll_area.h"
  14. #include "ui/wrap/vertical_layout.h"
  15. #include "webrtc/webrtc_device_common.h"
  16. #include "webrtc/webrtc_environment.h"
  17. #include "styles/style_calls.h"
  18. #include "styles/style_layers.h"
  19. namespace Calls {
  20. namespace {
  21. class Subsection final : public Ui::Menu::ItemBase {
  22. public:
  23. Subsection(
  24. not_null<RpWidget*> parent,
  25. const style::Menu &st,
  26. const QString &text);
  27. not_null<QAction*> action() const override;
  28. bool isEnabled() const override;
  29. private:
  30. int contentHeight() const override;
  31. const style::Menu &_st;
  32. const base::unique_qptr<Ui::FlatLabel> _text;
  33. const not_null<QAction*> _dummyAction;
  34. };
  35. class Selector final : public Ui::Menu::ItemBase {
  36. public:
  37. Selector(
  38. not_null<RpWidget*> parent,
  39. const style::Menu &st,
  40. rpl::producer<std::vector<Webrtc::DeviceInfo>> devices,
  41. rpl::producer<Webrtc::DeviceResolvedId> chosen,
  42. Fn<void(QString)> selected);
  43. not_null<QAction*> action() const override;
  44. bool isEnabled() const override;
  45. private:
  46. int contentHeight() const override;
  47. [[nodiscard]] int registerId(const QString &id);
  48. const base::unique_qptr<Ui::ScrollArea> _scroll;
  49. const not_null<Ui::VerticalLayout*> _list;
  50. const not_null<QAction*> _dummyAction;
  51. base::flat_map<QString, int> _ids;
  52. };
  53. Subsection::Subsection(
  54. not_null<RpWidget*> parent,
  55. const style::Menu &st,
  56. const QString &text)
  57. : Ui::Menu::ItemBase(parent, st)
  58. , _st(st)
  59. , _text(base::make_unique_q<Ui::FlatLabel>(
  60. this,
  61. text,
  62. st::callDeviceSelectionLabel))
  63. , _dummyAction(new QAction(parent)) {
  64. setPointerCursor(false);
  65. initResizeHook(parent->sizeValue());
  66. _text->resizeToWidth(st::callDeviceSelectionLabel.minWidth);
  67. _text->moveToLeft(st.itemPadding.left(), st.itemPadding.top());
  68. }
  69. not_null<QAction*> Subsection::action() const {
  70. return _dummyAction;
  71. }
  72. bool Subsection::isEnabled() const {
  73. return false;
  74. }
  75. int Subsection::contentHeight() const {
  76. return _st.itemPadding.top()
  77. + _text->height()
  78. + _st.itemPadding.bottom();
  79. }
  80. Selector::Selector(
  81. not_null<RpWidget*> parent,
  82. const style::Menu &st,
  83. rpl::producer<std::vector<Webrtc::DeviceInfo>> devices,
  84. rpl::producer<Webrtc::DeviceResolvedId> chosen,
  85. Fn<void(QString)> selected)
  86. : Ui::Menu::ItemBase(parent, st)
  87. , _scroll(base::make_unique_q<Ui::ScrollArea>(this))
  88. , _list(_scroll->setOwnedWidget(object_ptr<Ui::VerticalLayout>(this)))
  89. , _dummyAction(new QAction(parent)) {
  90. setPointerCursor(false);
  91. initResizeHook(parent->sizeValue());
  92. const auto padding = st.itemPadding;
  93. const auto group = std::make_shared<Ui::RadiobuttonGroup>();
  94. std::move(
  95. chosen
  96. ) | rpl::start_with_next([=](Webrtc::DeviceResolvedId id) {
  97. const auto value = id.isDefault() ? 0 : registerId(id.value);
  98. if (!group->hasValue() || group->current() != value) {
  99. group->setValue(value);
  100. }
  101. }, lifetime());
  102. group->setChangedCallback([=](int value) {
  103. if (value == 0) {
  104. selected({});
  105. } else {
  106. for (const auto &[id, index] : _ids) {
  107. if (index == value) {
  108. selected(id);
  109. break;
  110. }
  111. }
  112. }
  113. });
  114. std::move(
  115. devices
  116. ) | rpl::start_with_next([=](const std::vector<Webrtc::DeviceInfo> &v) {
  117. while (_list->count()) {
  118. delete _list->widgetAt(0);
  119. }
  120. _list->add(
  121. object_ptr<Ui::Radiobutton>(
  122. _list.get(),
  123. group,
  124. 0,
  125. tr::lng_settings_call_device_default(tr::now),
  126. st::groupCallCheckbox,
  127. st::groupCallRadio),
  128. padding);
  129. for (const auto &device : v) {
  130. if (device.inactive) {
  131. continue;
  132. }
  133. _list->add(
  134. object_ptr<Ui::Radiobutton>(
  135. _list.get(),
  136. group,
  137. registerId(device.id),
  138. device.name,
  139. st::groupCallCheckbox,
  140. st::groupCallRadio),
  141. padding);
  142. }
  143. resize(width(), contentHeight());
  144. }, lifetime());
  145. }
  146. not_null<QAction*> Selector::action() const {
  147. return _dummyAction;
  148. }
  149. bool Selector::isEnabled() const {
  150. return false;
  151. }
  152. int Selector::contentHeight() const {
  153. _list->resizeToWidth(width());
  154. if (_list->count() <= 3) {
  155. _scroll->resize(width(), _list->height());
  156. } else {
  157. _scroll->resize(
  158. width(),
  159. 3.5 * st::defaultRadio.diameter);
  160. }
  161. return _scroll->height();
  162. }
  163. int Selector::registerId(const QString &id) {
  164. auto &result = _ids[id];
  165. if (!result) {
  166. result = int(_ids.size());
  167. }
  168. return result;
  169. }
  170. void AddDeviceSelection(
  171. not_null<Ui::PopupMenu*> menu,
  172. not_null<Webrtc::Environment*> environment,
  173. DeviceSelection type,
  174. Fn<void(QString)> selected) {
  175. const auto title = [&] {
  176. switch (type.type) {
  177. case Webrtc::DeviceType::Camera:
  178. return tr::lng_settings_call_camera(tr::now);
  179. case Webrtc::DeviceType::Playback:
  180. return tr::lng_settings_call_section_output(tr::now);
  181. case Webrtc::DeviceType::Capture:
  182. return tr::lng_settings_call_section_input(tr::now);
  183. }
  184. Unexpected("Type in AddDeviceSelection.");
  185. }();
  186. menu->addAction(
  187. base::make_unique_q<Subsection>(menu, menu->st().menu, title));
  188. menu->addAction(
  189. base::make_unique_q<Selector>(
  190. menu,
  191. menu->st().menu,
  192. environment->devicesValue(type.type),
  193. std::move(type.chosen),
  194. selected));
  195. }
  196. } // namespace
  197. base::unique_qptr<Ui::PopupMenu> MakeDeviceSelectionMenu(
  198. not_null<Ui::RpWidget*> parent,
  199. not_null<Webrtc::Environment*> environment,
  200. std::vector<DeviceSelection> types,
  201. Fn<void(Webrtc::DeviceType, QString)> choose) {
  202. auto result = base::make_unique_q<Ui::PopupMenu>(
  203. parent,
  204. st::callDeviceSelectionMenu);
  205. const auto raw = result.get();
  206. for (auto type : types) {
  207. if (!raw->empty()) {
  208. raw->addSeparator();
  209. }
  210. const auto selected = [=, type = type.type](QString id) {
  211. choose(type, id);
  212. };
  213. AddDeviceSelection(raw, environment, std::move(type), selected);
  214. }
  215. return result;
  216. }
  217. } // namespace Calls