calls_top_bar.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776
  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/calls_top_bar.h"
  8. #include "ui/effects/cross_line.h"
  9. #include "ui/paint/blobs_linear.h"
  10. #include "ui/widgets/buttons.h"
  11. #include "ui/widgets/labels.h"
  12. #include "ui/chat/group_call_userpics.h" // Ui::GroupCallUser.
  13. #include "ui/chat/group_call_bar.h" // Ui::GroupCallBarContent.
  14. #include "ui/layers/generic_box.h"
  15. #include "ui/wrap/padding_wrap.h"
  16. #include "ui/text/format_values.h"
  17. #include "ui/toast/toast.h"
  18. #include "ui/power_saving.h"
  19. #include "lang/lang_keys.h"
  20. #include "core/application.h"
  21. #include "calls/calls_call.h"
  22. #include "calls/calls_instance.h"
  23. #include "calls/calls_signal_bars.h"
  24. #include "calls/group/calls_group_call.h"
  25. #include "calls/group/calls_group_menu.h" // Group::LeaveBox.
  26. #include "history/view/history_view_group_call_bar.h" // ContentByCall.
  27. #include "data/data_user.h"
  28. #include "data/data_group_call.h"
  29. #include "data/data_peer.h"
  30. #include "data/data_changes.h"
  31. #include "main/main_session.h"
  32. #include "boxes/abstract_box.h"
  33. #include "base/timer.h"
  34. #include "styles/style_basic.h"
  35. #include "styles/style_calls.h"
  36. #include "styles/style_chat_helpers.h" // style::GroupCallUserpics
  37. #include "styles/style_layers.h"
  38. namespace Calls {
  39. enum class BarState {
  40. Connecting,
  41. Active,
  42. Muted,
  43. ForceMuted,
  44. };
  45. namespace {
  46. constexpr auto kUpdateDebugTimeoutMs = crl::time(500);
  47. constexpr auto kMinorBlobAlpha = 76. / 255.;
  48. constexpr auto kHideBlobsDuration = crl::time(500);
  49. constexpr auto kBlobLevelDuration = crl::time(250);
  50. constexpr auto kBlobUpdateInterval = crl::time(100);
  51. auto BarStateFromMuteState(
  52. MuteState state,
  53. GroupCall::InstanceState instanceState,
  54. TimeId scheduledDate) {
  55. return scheduledDate
  56. ? BarState::ForceMuted
  57. : (instanceState == GroupCall::InstanceState::Disconnected)
  58. ? BarState::Connecting
  59. : (state == MuteState::ForceMuted || state == MuteState::RaisedHand)
  60. ? BarState::ForceMuted
  61. : (state == MuteState::Muted)
  62. ? BarState::Muted
  63. : BarState::Active;
  64. };
  65. auto LinearBlobs() {
  66. return std::vector<Ui::Paint::LinearBlobs::BlobData>{
  67. {
  68. .segmentsCount = 5,
  69. .minRadius = 0.,
  70. .maxRadius = (float)st::groupCallMajorBlobMaxRadius,
  71. .idleRadius = (float)st::groupCallMinorBlobIdleRadius,
  72. .speedScale = .3,
  73. .alpha = 1.,
  74. },
  75. {
  76. .segmentsCount = 7,
  77. .minRadius = 0.,
  78. .maxRadius = (float)st::groupCallMinorBlobMaxRadius,
  79. .idleRadius = (float)st::groupCallMinorBlobIdleRadius,
  80. .speedScale = .7,
  81. .alpha = kMinorBlobAlpha,
  82. },
  83. {
  84. .segmentsCount = 8,
  85. .minRadius = 0.,
  86. .maxRadius = (float)st::groupCallMinorBlobMaxRadius,
  87. .idleRadius = (float)st::groupCallMinorBlobIdleRadius,
  88. .speedScale = .7,
  89. .alpha = kMinorBlobAlpha,
  90. },
  91. };
  92. }
  93. auto Colors() {
  94. using Vector = std::vector<QColor>;
  95. using Colors = anim::gradient_colors;
  96. return base::flat_map<BarState, Colors>{
  97. {
  98. BarState::ForceMuted,
  99. Colors(QGradientStops{
  100. { 0.0, st::groupCallForceMutedBar1->c },
  101. { .35, st::groupCallForceMutedBar2->c },
  102. { 1.0, st::groupCallForceMutedBar3->c } })
  103. },
  104. {
  105. BarState::Active,
  106. Colors(Vector{ st::groupCallLive1->c, st::groupCallLive2->c })
  107. },
  108. {
  109. BarState::Muted,
  110. Colors(Vector{ st::groupCallMuted1->c, st::groupCallMuted2->c })
  111. },
  112. {
  113. BarState::Connecting,
  114. Colors(st::callBarBgMuted->c)
  115. },
  116. };
  117. }
  118. class DebugInfoBox : public Ui::BoxContent {
  119. public:
  120. DebugInfoBox(QWidget*, base::weak_ptr<Call> call);
  121. protected:
  122. void prepare() override;
  123. private:
  124. void updateText();
  125. base::weak_ptr<Call> _call;
  126. QPointer<Ui::FlatLabel> _text;
  127. base::Timer _updateTextTimer;
  128. };
  129. DebugInfoBox::DebugInfoBox(QWidget*, base::weak_ptr<Call> call)
  130. : _call(call) {
  131. }
  132. void DebugInfoBox::prepare() {
  133. setTitle(rpl::single(u"Call Debug"_q));
  134. addButton(tr::lng_close(), [this] { closeBox(); });
  135. _text = setInnerWidget(
  136. object_ptr<Ui::PaddingWrap<Ui::FlatLabel>>(
  137. this,
  138. object_ptr<Ui::FlatLabel>(this, st::callDebugLabel),
  139. st::callDebugPadding))->entity();
  140. _text->setSelectable(true);
  141. updateText();
  142. _updateTextTimer.setCallback([this] { updateText(); });
  143. _updateTextTimer.callEach(kUpdateDebugTimeoutMs);
  144. setDimensions(st::boxWideWidth, st::boxMaxListHeight);
  145. }
  146. void DebugInfoBox::updateText() {
  147. if (auto call = _call.get()) {
  148. _text->setText(call->getDebugLog());
  149. }
  150. }
  151. } // namespace
  152. struct TopBar::User {
  153. Ui::GroupCallUser data;
  154. };
  155. class Mute final : public Ui::IconButton {
  156. public:
  157. Mute(QWidget *parent, const style::IconButton &st)
  158. : Ui::IconButton(parent, st)
  159. , _st(st)
  160. , _crossLineMuteAnimation(st::callTopBarMuteCrossLine) {
  161. resize(_st.width, _st.height);
  162. installEventFilter(this);
  163. style::PaletteChanged(
  164. ) | rpl::start_with_next([=] {
  165. _crossLineMuteAnimation.invalidate();
  166. }, lifetime());
  167. }
  168. void setProgress(float64 progress) {
  169. if (_progress == progress) {
  170. return;
  171. }
  172. _progress = progress;
  173. update();
  174. }
  175. void setRippleColorOverride(const style::color *colorOverride) {
  176. _rippleColorOverride = colorOverride;
  177. }
  178. protected:
  179. bool eventFilter(QObject *object, QEvent *event) {
  180. if (event->type() == QEvent::Paint) {
  181. auto p = QPainter(this);
  182. paintRipple(
  183. p,
  184. _st.rippleAreaPosition.x(),
  185. _st.rippleAreaPosition.y(),
  186. _rippleColorOverride ? &(*_rippleColorOverride)->c : nullptr);
  187. _crossLineMuteAnimation.paint(p, _st.iconPosition, _progress);
  188. return true;
  189. }
  190. return QObject::eventFilter(object, event);
  191. }
  192. private:
  193. float64 _progress = 0.;
  194. const style::IconButton &_st;
  195. Ui::CrossLineAnimation _crossLineMuteAnimation;
  196. const style::color *_rippleColorOverride = nullptr;
  197. };
  198. TopBar::TopBar(
  199. QWidget *parent,
  200. const base::weak_ptr<Call> &call,
  201. std::shared_ptr<Ui::Show> show)
  202. : TopBar(parent, show, call, nullptr) {
  203. }
  204. TopBar::TopBar(
  205. QWidget *parent,
  206. const base::weak_ptr<GroupCall> &call,
  207. std::shared_ptr<Ui::Show> show)
  208. : TopBar(parent, show, nullptr, call) {
  209. }
  210. TopBar::TopBar(
  211. QWidget *parent,
  212. std::shared_ptr<Ui::Show> show,
  213. const base::weak_ptr<Call> &call,
  214. const base::weak_ptr<GroupCall> &groupCall)
  215. : RpWidget(parent)
  216. , _call(call)
  217. , _groupCall(groupCall)
  218. , _show(show)
  219. , _userpics(call
  220. ? nullptr
  221. : std::make_unique<Ui::GroupCallUserpics>(
  222. st::groupCallTopBarUserpics,
  223. rpl::single(true),
  224. [=] { updateUserpics(); }))
  225. , _durationLabel(_call
  226. ? object_ptr<Ui::LabelSimple>(this, st::callBarLabel)
  227. : object_ptr<Ui::LabelSimple>(nullptr))
  228. , _signalBars(_call
  229. ? object_ptr<SignalBars>(this, _call.get(), st::callBarSignalBars)
  230. : object_ptr<SignalBars>(nullptr))
  231. , _fullInfoLabel(this, st::callBarInfoLabel)
  232. , _shortInfoLabel(this, st::callBarInfoLabel)
  233. , _hangupLabel(_call
  234. ? object_ptr<Ui::LabelSimple>(
  235. this,
  236. st::callBarLabel,
  237. tr::lng_call_bar_hangup(tr::now))
  238. : object_ptr<Ui::LabelSimple>(nullptr))
  239. , _mute(this, st::callBarMuteToggle)
  240. , _info(this)
  241. , _hangup(this, st::callBarHangup)
  242. , _gradients(Colors(), QPointF(), QPointF())
  243. , _updateDurationTimer([=] { updateDurationText(); }) {
  244. initControls();
  245. resize(width(), st::callBarHeight);
  246. setupInitialBrush();
  247. }
  248. void TopBar::setupInitialBrush() {
  249. Expects(_switchStateCallback != nullptr);
  250. _switchStateAnimation.stop();
  251. _switchStateCallback(1.);
  252. }
  253. void TopBar::initControls() {
  254. _mute->setClickedCallback([=] {
  255. if (const auto call = _call.get()) {
  256. call->setMuted(!call->muted());
  257. } else if (const auto group = _groupCall.get()) {
  258. if (group->mutedByAdmin()) {
  259. _show->showToast(
  260. tr::lng_group_call_force_muted_sub(tr::now));
  261. } else {
  262. group->setMuted((group->muted() == MuteState::Muted)
  263. ? MuteState::Active
  264. : MuteState::Muted);
  265. }
  266. }
  267. });
  268. const auto mapToState = [](bool muted) {
  269. return muted ? MuteState::Muted : MuteState::Active;
  270. };
  271. const auto fromState = _mute->lifetime().make_state<BarState>(
  272. BarStateFromMuteState(
  273. _call
  274. ? mapToState(_call->muted())
  275. : _groupCall->muted(),
  276. GroupCall::InstanceState::Connected,
  277. _call ? TimeId(0) : _groupCall->scheduleDate()));
  278. using namespace rpl::mappers;
  279. auto muted = _call
  280. ? rpl::combine(
  281. _call->mutedValue() | rpl::map(mapToState),
  282. rpl::single(GroupCall::InstanceState::Connected),
  283. rpl::single(TimeId(0))
  284. ) | rpl::type_erased()
  285. : rpl::combine(
  286. (_groupCall->mutedValue()
  287. | MapPushToTalkToActive()
  288. | rpl::distinct_until_changed()
  289. | rpl::type_erased()),
  290. rpl::single(
  291. _groupCall->instanceState()
  292. ) | rpl::then(_groupCall->instanceStateValue() | rpl::filter(
  293. _1 != GroupCall::InstanceState::TransitionToRtc)),
  294. rpl::single(
  295. _groupCall->scheduleDate()
  296. ) | rpl::then(_groupCall->real(
  297. ) | rpl::map([](not_null<Data::GroupCall*> call) {
  298. return call->scheduleDateValue();
  299. }) | rpl::flatten_latest()));
  300. std::move(
  301. muted
  302. ) | rpl::map(
  303. BarStateFromMuteState
  304. ) | rpl::start_with_next([=](BarState state) {
  305. _isGroupConnecting = (state == BarState::Connecting);
  306. setMuted(state != BarState::Active);
  307. update();
  308. const auto isForceMuted = (state == BarState::ForceMuted);
  309. if (isForceMuted) {
  310. _mute->clearState();
  311. }
  312. _mute->setPointerCursor(!isForceMuted);
  313. const auto to = 1.;
  314. const auto from = _switchStateAnimation.animating()
  315. ? (to - _switchStateAnimation.value(0.))
  316. : 0.;
  317. const auto fromMuted = *fromState;
  318. const auto toMuted = state;
  319. *fromState = state;
  320. const auto crossFrom = (fromMuted != BarState::Active) ? 1. : 0.;
  321. const auto crossTo = (toMuted != BarState::Active) ? 1. : 0.;
  322. _switchStateCallback = [=](float64 value) {
  323. if (_groupCall) {
  324. _groupBrush = QBrush(
  325. _gradients.gradient(fromMuted, toMuted, value));
  326. update();
  327. }
  328. const auto crossProgress = (crossFrom == crossTo)
  329. ? crossTo
  330. : anim::interpolateToF(crossFrom, crossTo, value);
  331. _mute->setProgress(crossProgress);
  332. };
  333. _switchStateAnimation.stop();
  334. const auto duration = (to - from) * st::universalDuration;
  335. _switchStateAnimation.start(
  336. _switchStateCallback,
  337. from,
  338. to,
  339. duration);
  340. }, _mute->lifetime());
  341. if (const auto group = _groupCall.get()) {
  342. subscribeToMembersChanges(group);
  343. _isGroupConnecting.value(
  344. ) | rpl::start_with_next([=](bool isConnecting) {
  345. _mute->setAttribute(
  346. Qt::WA_TransparentForMouseEvents,
  347. isConnecting);
  348. updateInfoLabels();
  349. }, lifetime());
  350. }
  351. if (const auto call = _call.get()) {
  352. call->user()->session().changes().peerUpdates(
  353. Data::PeerUpdate::Flag::Name
  354. ) | rpl::filter([=](const Data::PeerUpdate &update) {
  355. // _user may change for the same Panel.
  356. return (_call != nullptr) && (update.peer == _call->user());
  357. }) | rpl::start_with_next([=] {
  358. updateInfoLabels();
  359. }, lifetime());
  360. }
  361. setInfoLabels();
  362. _info->setClickedCallback([=] {
  363. if (const auto call = _call.get()) {
  364. if (Logs::DebugEnabled()
  365. && (_info->clickModifiers() & Qt::ControlModifier)) {
  366. _show->showBox(
  367. Box<DebugInfoBox>(_call),
  368. Ui::LayerOption::CloseOther);
  369. } else {
  370. Core::App().calls().showInfoPanel(call);
  371. }
  372. } else if (const auto group = _groupCall.get()) {
  373. Core::App().calls().showInfoPanel(group);
  374. }
  375. });
  376. _hangup->setClickedCallback([this] {
  377. if (const auto call = _call.get()) {
  378. call->hangup();
  379. } else if (const auto group = _groupCall.get()) {
  380. if (!group->peer()->canManageGroupCall()) {
  381. group->hangup();
  382. } else {
  383. _show->showBox(
  384. Box(
  385. Group::LeaveBox,
  386. group,
  387. false,
  388. Group::BoxContext::MainWindow),
  389. Ui::LayerOption::CloseOther);
  390. }
  391. }
  392. });
  393. updateDurationText();
  394. }
  395. void TopBar::initBlobsUnder(
  396. QWidget *blobsParent,
  397. rpl::producer<QRect> barGeometry) {
  398. const auto group = _groupCall.get();
  399. if (!group) {
  400. return;
  401. }
  402. struct State {
  403. Ui::Paint::LinearBlobs paint = {
  404. LinearBlobs(),
  405. kBlobLevelDuration,
  406. 1.,
  407. Ui::Paint::LinearBlob::Direction::TopDown
  408. };
  409. Ui::Animations::Simple hideAnimation;
  410. Ui::Animations::Basic animation;
  411. base::Timer levelTimer;
  412. crl::time hideLastTime = 0;
  413. crl::time lastTime = 0;
  414. float lastLevel = 0.;
  415. float levelBeforeLast = 0.;
  416. };
  417. _blobs = base::make_unique_q<Ui::RpWidget>(blobsParent);
  418. const auto state = _blobs->lifetime().make_state<State>();
  419. state->levelTimer.setCallback([=] {
  420. state->levelBeforeLast = state->lastLevel;
  421. state->lastLevel = 0.;
  422. if (state->levelBeforeLast == 0.) {
  423. state->paint.setLevel(0.);
  424. state->levelTimer.cancel();
  425. }
  426. });
  427. state->animation.init([=](crl::time now) {
  428. if (const auto last = state->hideLastTime; (last > 0)
  429. && (now - last >= kHideBlobsDuration)) {
  430. state->animation.stop();
  431. return false;
  432. }
  433. state->paint.updateLevel(now - state->lastTime);
  434. state->lastTime = now;
  435. _blobs->update();
  436. return true;
  437. });
  438. group->stateValue(
  439. ) | rpl::start_with_next([=](Calls::GroupCall::State state) {
  440. if (state == Calls::GroupCall::State::HangingUp) {
  441. _blobs->hide();
  442. }
  443. }, lifetime());
  444. using namespace rpl::mappers;
  445. auto hideBlobs = rpl::combine(
  446. PowerSaving::OnValue(PowerSaving::kCalls),
  447. Core::App().appDeactivatedValue(),
  448. group->instanceStateValue()
  449. ) | rpl::map(_1 || _2 || _3 == GroupCall::InstanceState::Disconnected);
  450. std::move(
  451. hideBlobs
  452. ) | rpl::distinct_until_changed(
  453. ) | rpl::start_with_next([=](bool hide) {
  454. if (hide) {
  455. state->paint.setLevel(0.);
  456. }
  457. state->hideLastTime = hide ? crl::now() : 0;
  458. if (!hide && !state->animation.animating()) {
  459. state->animation.start();
  460. }
  461. if (hide) {
  462. state->levelTimer.cancel();
  463. } else {
  464. state->lastLevel = 0.;
  465. }
  466. const auto from = hide ? 0. : 1.;
  467. const auto to = hide ? 1. : 0.;
  468. state->hideAnimation.start([=](float64) {
  469. _blobs->update();
  470. }, from, to, kHideBlobsDuration);
  471. }, lifetime());
  472. std::move(
  473. barGeometry
  474. ) | rpl::start_with_next([=](QRect rect) {
  475. _blobs->resize(
  476. rect.width(),
  477. (int)state->paint.maxRadius());
  478. _blobs->moveToLeft(rect.x(), rect.y() + rect.height());
  479. }, lifetime());
  480. shownValue(
  481. ) | rpl::start_with_next([=](bool shown) {
  482. _blobs->setVisible(shown);
  483. }, lifetime());
  484. _blobs->paintRequest(
  485. ) | rpl::start_with_next([=](QRect clip) {
  486. const auto hidden = state->hideAnimation.value(
  487. state->hideLastTime ? 1. : 0.);
  488. if (hidden == 1.) {
  489. return;
  490. }
  491. auto p = QPainter(_blobs);
  492. if (hidden > 0.) {
  493. p.setOpacity(1. - hidden);
  494. }
  495. const auto top = -_blobs->height() * hidden;
  496. const auto width = _blobs->width();
  497. p.translate(0, top);
  498. state->paint.paint(p, _groupBrush, width);
  499. }, _blobs->lifetime());
  500. group->levelUpdates(
  501. ) | rpl::filter([=](const LevelUpdate &update) {
  502. return !state->hideLastTime && (update.value > state->lastLevel);
  503. }) | rpl::start_with_next([=](const LevelUpdate &update) {
  504. if (state->lastLevel == 0.) {
  505. state->levelTimer.callEach(kBlobUpdateInterval);
  506. }
  507. state->lastLevel = update.value;
  508. state->paint.setLevel(update.value);
  509. }, _blobs->lifetime());
  510. _blobs->setAttribute(Qt::WA_TransparentForMouseEvents);
  511. _blobs->show();
  512. if (!state->hideLastTime) {
  513. state->animation.start();
  514. }
  515. }
  516. void TopBar::subscribeToMembersChanges(not_null<GroupCall*> call) {
  517. const auto peer = call->peer();
  518. peer->session().changes().peerFlagsValue(
  519. peer,
  520. Data::PeerUpdate::Flag::GroupCall
  521. ) | rpl::map([=] {
  522. return peer->groupCall();
  523. }) | rpl::filter([=](Data::GroupCall *real) {
  524. const auto call = _groupCall.get();
  525. return call && real && (real->id() == call->id());
  526. }) | rpl::take(
  527. 1
  528. ) | rpl::before_next([=](not_null<Data::GroupCall*> real) {
  529. real->titleValue() | rpl::start_with_next([=] {
  530. updateInfoLabels();
  531. }, lifetime());
  532. }) | rpl::map([=](not_null<Data::GroupCall*> real) {
  533. return HistoryView::GroupCallBarContentByCall(
  534. real,
  535. st::groupCallTopBarUserpics.size);
  536. }) | rpl::flatten_latest(
  537. ) | rpl::filter([=](const Ui::GroupCallBarContent &content) {
  538. if (_users.size() != content.users.size()) {
  539. return true;
  540. }
  541. for (auto i = 0, count = int(_users.size()); i != count; ++i) {
  542. if (_users[i].userpicKey != content.users[i].userpicKey
  543. || _users[i].id != content.users[i].id) {
  544. return true;
  545. }
  546. }
  547. return false;
  548. }) | rpl::start_with_next([=](const Ui::GroupCallBarContent &content) {
  549. _users = content.users;
  550. for (auto &user : _users) {
  551. user.speaking = false;
  552. }
  553. _userpics->update(_users, !isHidden());
  554. }, lifetime());
  555. _userpics->widthValue(
  556. ) | rpl::start_with_next([=](int width) {
  557. _userpicsWidth = width;
  558. updateControlsGeometry();
  559. }, lifetime());
  560. call->peer()->session().changes().peerUpdates(
  561. Data::PeerUpdate::Flag::Name
  562. ) | rpl::filter([=](const Data::PeerUpdate &update) {
  563. // _peer may change for the same Panel.
  564. const auto call = _groupCall.get();
  565. return (call != nullptr) && (update.peer == call->peer());
  566. }) | rpl::start_with_next([=] {
  567. updateInfoLabels();
  568. }, lifetime());
  569. }
  570. void TopBar::updateUserpics() {
  571. update(_mute->width(), 0, _userpics->maxWidth(), height());
  572. }
  573. void TopBar::updateInfoLabels() {
  574. setInfoLabels();
  575. updateControlsGeometry();
  576. }
  577. void TopBar::setInfoLabels() {
  578. if (const auto call = _call.get()) {
  579. const auto user = call->user();
  580. const auto fullName = user->name();
  581. const auto shortName = user->firstName;
  582. _fullInfoLabel->setText(fullName);
  583. _shortInfoLabel->setText(shortName);
  584. } else if (const auto group = _groupCall.get()) {
  585. const auto peer = group->peer();
  586. const auto real = peer->groupCall();
  587. const auto name = peer->name();
  588. const auto text = _isGroupConnecting.current()
  589. ? tr::lng_group_call_connecting(tr::now)
  590. : (real && real->id() == group->id() && !real->title().isEmpty())
  591. ? real->title()
  592. : name;
  593. _fullInfoLabel->setText(text);
  594. _shortInfoLabel->setText(text);
  595. }
  596. }
  597. void TopBar::setMuted(bool mute) {
  598. _mute->setRippleColorOverride(&st::shadowFg);
  599. _hangup->setRippleColorOverride(&st::shadowFg);
  600. _muted = mute;
  601. }
  602. void TopBar::updateDurationText() {
  603. if (!_call || !_durationLabel) {
  604. return;
  605. }
  606. auto wasWidth = _durationLabel->width();
  607. auto durationMs = _call->getDurationMs();
  608. auto durationSeconds = durationMs / 1000;
  609. startDurationUpdateTimer(durationMs);
  610. _durationLabel->setText(Ui::FormatDurationText(durationSeconds));
  611. if (_durationLabel->width() != wasWidth) {
  612. updateControlsGeometry();
  613. }
  614. }
  615. void TopBar::startDurationUpdateTimer(crl::time currentDuration) {
  616. auto msTillNextSecond = 1000 - (currentDuration % 1000);
  617. _updateDurationTimer.callOnce(msTillNextSecond + 5);
  618. }
  619. void TopBar::resizeEvent(QResizeEvent *e) {
  620. updateControlsGeometry();
  621. }
  622. void TopBar::updateControlsGeometry() {
  623. auto left = 0;
  624. _mute->moveToLeft(left, 0);
  625. left += _mute->width();
  626. if (_durationLabel) {
  627. _durationLabel->moveToLeft(left, st::callBarLabelTop);
  628. left += _durationLabel->width() + st::callBarSkip;
  629. }
  630. if (_userpicsWidth) {
  631. const auto single = st::groupCallTopBarUserpics.size;
  632. const auto skip = anim::interpolate(
  633. 0,
  634. st::callBarSkip,
  635. std::min(_userpicsWidth, single) / float64(single));
  636. left += _userpicsWidth + skip;
  637. }
  638. if (_signalBars) {
  639. _signalBars->moveToLeft(left, (height() - _signalBars->height()) / 2);
  640. left += _signalBars->width() + st::callBarSkip;
  641. }
  642. auto right = st::callBarRightSkip;
  643. if (_hangupLabel) {
  644. _hangupLabel->moveToRight(right, st::callBarLabelTop);
  645. right += _hangupLabel->width();
  646. } else {
  647. //right -= st::callBarRightSkip;
  648. }
  649. right += st::callBarHangup.width;
  650. _hangup->setGeometryToRight(0, 0, right, height());
  651. _info->setGeometryToLeft(
  652. _mute->width(),
  653. 0,
  654. width() - _mute->width() - _hangup->width(),
  655. height());
  656. auto fullWidth = _fullInfoLabel->textMaxWidth();
  657. auto showFull = (left + fullWidth + right <= width());
  658. _fullInfoLabel->setVisible(showFull);
  659. _shortInfoLabel->setVisible(!showFull);
  660. auto setInfoLabelGeometry = [this, left, right](auto &&infoLabel) {
  661. auto minPadding = qMax(left, right);
  662. auto infoWidth = infoLabel->textMaxWidth();
  663. auto infoLeft = (width() - infoWidth) / 2;
  664. if (infoLeft < minPadding) {
  665. infoLeft = left;
  666. infoWidth = width() - left - right;
  667. }
  668. infoLabel->setGeometryToLeft(infoLeft, st::callBarLabelTop, infoWidth, st::callBarInfoLabel.style.font->height);
  669. };
  670. setInfoLabelGeometry(_fullInfoLabel);
  671. setInfoLabelGeometry(_shortInfoLabel);
  672. _gradients.set_points(
  673. QPointF(0, st::callBarHeight / 2),
  674. QPointF(width(), st::callBarHeight / 2));
  675. if (!_switchStateAnimation.animating()) {
  676. _switchStateCallback(1.);
  677. }
  678. }
  679. void TopBar::paintEvent(QPaintEvent *e) {
  680. auto p = QPainter(this);
  681. auto brush = _groupCall
  682. ? _groupBrush
  683. : (_muted ? st::callBarBgMuted : st::callBarBg);
  684. p.fillRect(e->rect(), std::move(brush));
  685. if (_userpicsWidth) {
  686. const auto size = st::groupCallTopBarUserpics.size;
  687. const auto top = (height() - size) / 2;
  688. _userpics->paint(p, _mute->width(), top, size);
  689. }
  690. }
  691. TopBar::~TopBar() = default;
  692. } // namespace Calls