media_player_widget.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784
  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 "media/player/media_player_widget.h"
  8. #include "platform/platform_specific.h"
  9. #include "data/data_document.h"
  10. #include "data/data_session.h"
  11. #include "data/data_peer.h"
  12. #include "core/application.h"
  13. #include "core/core_settings.h"
  14. #include "ui/widgets/labels.h"
  15. #include "ui/widgets/continuous_sliders.h"
  16. #include "ui/widgets/shadow.h"
  17. #include "ui/widgets/buttons.h"
  18. #include "ui/widgets/popup_menu.h"
  19. #include "ui/widgets/dropdown_menu.h"
  20. #include "ui/widgets/menu/menu_action.h"
  21. #include "ui/wrap/fade_wrap.h"
  22. #include "ui/effects/ripple_animation.h"
  23. #include "ui/text/format_values.h"
  24. #include "ui/text/format_song_document_name.h"
  25. #include "lang/lang_keys.h"
  26. #include "media/audio/media_audio.h"
  27. #include "media/view/media_view_playback_progress.h"
  28. #include "media/player/media_player_button.h"
  29. #include "media/player/media_player_instance.h"
  30. #include "media/player/media_player_dropdown.h"
  31. #include "media/player/media_player_volume_controller.h"
  32. #include "history/history_item.h"
  33. #include "history/history_item_helpers.h"
  34. #include "storage/storage_account.h"
  35. #include "main/main_session.h"
  36. #include "window/window_session_controller.h"
  37. #include "styles/style_media_player.h"
  38. #include "styles/style_media_view.h"
  39. #include "styles/style_chat.h" // expandedMenuSeparator.
  40. namespace Media {
  41. namespace Player {
  42. Widget::Widget(
  43. QWidget *parent,
  44. not_null<Ui::RpWidget*> dropdownsParent,
  45. not_null<Window::SessionController*> controller)
  46. : RpWidget(parent)
  47. , _controller(controller)
  48. , _orderMenuParent(dropdownsParent)
  49. , _nameLabel(this, st::mediaPlayerName)
  50. , _rightControls(this, object_ptr<Ui::RpWidget>(this))
  51. , _timeLabel(rightControls(), st::mediaPlayerTime)
  52. , _playPause(this, st::mediaPlayerPlayButton)
  53. , _volumeToggle(rightControls(), st::mediaPlayerVolumeToggle)
  54. , _repeatToggle(rightControls(), st::mediaPlayerRepeatButton)
  55. , _orderToggle(rightControls(), st::mediaPlayerOrderButton)
  56. , _speedToggle(rightControls(), st::mediaPlayerSpeedButton)
  57. , _close(this, st::mediaPlayerClose)
  58. , _shadow(this)
  59. , _playbackSlider(this, st::mediaPlayerPlayback)
  60. , _volume(std::in_place, dropdownsParent.get())
  61. , _playbackProgress(std::make_unique<View::PlaybackProgress>())
  62. , _orderController(
  63. std::make_unique<OrderController>(
  64. _orderToggle.data(),
  65. dropdownsParent,
  66. [=](bool over) { markOver(over); },
  67. Core::App().settings().playerOrderModeValue(),
  68. [=](OrderMode value) { saveOrder(value); }))
  69. , _speedController(
  70. std::make_unique<SpeedController>(
  71. _speedToggle.data(),
  72. _speedToggle->st(),
  73. dropdownsParent,
  74. [=](bool over) { markOver(over); },
  75. [=](bool lastNonDefault) { return speedLookup(lastNonDefault); },
  76. [=](float64 speed) { saveSpeed(speed); })) {
  77. _speedController->realtimeValue(
  78. ) | rpl::start_with_next([=](float64 speed) {
  79. _speedToggle->setSpeed(speed);
  80. }, _speedToggle->lifetime());
  81. _speedToggle->finishAnimating();
  82. setAttribute(Qt::WA_OpaquePaintEvent);
  83. setMouseTracking(true);
  84. resize(width(), st::mediaPlayerHeight + st::lineWidth);
  85. setupRightControls();
  86. _nameLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
  87. _timeLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
  88. _playbackProgress->setInLoadingStateChangedCallback([=](bool loading) {
  89. _playbackSlider->setDisabled(loading);
  90. });
  91. _playbackProgress->setValueChangedCallback([=](float64 value, float64) {
  92. _playbackSlider->setValue(value);
  93. });
  94. _playbackSlider->setChangeProgressCallback([=](float64 value) {
  95. if (_type != AudioMsgId::Type::Song) {
  96. return; // Round video seek is not supported for now :(
  97. }
  98. _playbackProgress->setValue(value, false);
  99. handleSeekProgress(value);
  100. });
  101. _playbackSlider->setChangeFinishedCallback([=](float64 value) {
  102. if (_type != AudioMsgId::Type::Song) {
  103. return; // Round video seek is not supported for now :(
  104. }
  105. _playbackProgress->setValue(value, false);
  106. handleSeekFinished(value);
  107. });
  108. _playPause->setClickedCallback([=] {
  109. instance()->playPauseCancelClicked(_type);
  110. });
  111. updateVolumeToggleIcon();
  112. _volumeToggle->setClickedCallback([=] {
  113. const auto volume = (Core::App().settings().songVolume() > 0)
  114. ? 0.
  115. : Core::App().settings().rememberedSongVolume();
  116. Core::App().settings().setSongVolume(volume);
  117. Core::App().saveSettingsDelayed();
  118. mixer()->setSongVolume(volume);
  119. });
  120. Core::App().settings().songVolumeChanges(
  121. ) | rpl::start_with_next([=] {
  122. updateVolumeToggleIcon();
  123. }, lifetime());
  124. Core::App().settings().playerRepeatModeValue(
  125. ) | rpl::start_with_next([=] {
  126. updateRepeatToggleIcon();
  127. }, lifetime());
  128. _repeatToggle->setClickedCallback([=] {
  129. auto &settings = Core::App().settings();
  130. settings.setPlayerRepeatMode([&] {
  131. switch (settings.playerRepeatMode()) {
  132. case RepeatMode::None: return RepeatMode::One;
  133. case RepeatMode::One: return RepeatMode::All;
  134. case RepeatMode::All: return RepeatMode::None;
  135. }
  136. Unexpected("Repeat mode in Settings.");
  137. }());
  138. Core::App().saveSettingsDelayed();
  139. });
  140. _speedController->saved(
  141. ) | rpl::start_with_next([=] {
  142. instance()->updateVoicePlaybackSpeed();
  143. }, lifetime());
  144. instance()->trackChanged(
  145. ) | rpl::filter([=](AudioMsgId::Type type) {
  146. return (type == _type);
  147. }) | rpl::start_with_next([=](AudioMsgId::Type type) {
  148. handleSongChange();
  149. updateControlsVisibility();
  150. updateLabelsGeometry();
  151. }, lifetime());
  152. instance()->tracksFinished(
  153. ) | rpl::filter([=](AudioMsgId::Type type) {
  154. return (type == AudioMsgId::Type::Voice);
  155. }) | rpl::start_with_next([=](AudioMsgId::Type type) {
  156. _voiceIsActive = false;
  157. const auto currentSong = instance()->current(AudioMsgId::Type::Song);
  158. const auto songState = instance()->getState(AudioMsgId::Type::Song);
  159. if (currentSong == songState.id && !IsStoppedOrStopping(songState.state)) {
  160. setType(AudioMsgId::Type::Song);
  161. }
  162. }, lifetime());
  163. instance()->updatedNotifier(
  164. ) | rpl::start_with_next([=](const TrackState &state) {
  165. handleSongUpdate(state);
  166. }, lifetime());
  167. PrepareVolumeDropdown(_volume.get(), controller, _volumeToggle->events(
  168. ) | rpl::filter([=](not_null<QEvent*> e) {
  169. return (e->type() == QEvent::Wheel);
  170. }) | rpl::map([=](not_null<QEvent*> e) {
  171. return not_null{ static_cast<QWheelEvent*>(e.get()) };
  172. }));
  173. _volumeToggle->installEventFilter(_volume.get());
  174. _volume->events(
  175. ) | rpl::start_with_next([=](not_null<QEvent*> e) {
  176. if (e->type() == QEvent::Enter) {
  177. markOver(true);
  178. } else if (e->type() == QEvent::Leave) {
  179. markOver(false);
  180. }
  181. }, _volume->lifetime());
  182. hidePlaylistOn(_playPause);
  183. hidePlaylistOn(_close);
  184. hidePlaylistOn(_rightControls);
  185. setType(AudioMsgId::Type::Song);
  186. }
  187. void Widget::hidePlaylistOn(not_null<Ui::RpWidget*> widget) {
  188. widget->events(
  189. ) | rpl::filter([=](not_null<QEvent*> e) {
  190. return (e->type() == QEvent::Enter);
  191. }) | rpl::start_with_next([=] {
  192. updateOverLabelsState(false);
  193. }, widget->lifetime());
  194. }
  195. void Widget::setupRightControls() {
  196. const auto raw = rightControls();
  197. raw->paintRequest(
  198. ) | rpl::start_with_next([=](QRect clip) {
  199. auto p = QPainter(raw);
  200. const auto &icon = st::mediaPlayerControlsFade;
  201. const auto fade = QRect(0, 0, icon.width(), raw->height());
  202. if (fade.intersects(clip)) {
  203. icon.fill(p, fade);
  204. }
  205. const auto fill = clip.intersected(
  206. { icon.width(), 0, raw->width() - icon.width(), raw->height() });
  207. if (!fill.isEmpty()) {
  208. p.fillRect(fill, st::mediaPlayerBg);
  209. }
  210. }, raw->lifetime());
  211. _rightControls->show(anim::type::instant);
  212. }
  213. void Widget::updateVolumeToggleIcon() {
  214. _volumeToggle->setIconOverride([] {
  215. const auto volume = Core::App().settings().songVolume();
  216. return (volume == 0.)
  217. ? &st::mediaPlayerVolumeIcon0
  218. : (volume < 0.66)
  219. ? &st::mediaPlayerVolumeIcon1
  220. : nullptr;
  221. }());
  222. }
  223. void Widget::setCloseCallback(Fn<void()> callback) {
  224. _closeCallback = std::move(callback);
  225. _close->setClickedCallback([this] { stopAndClose(); });
  226. }
  227. void Widget::setShowItemCallback(
  228. Fn<void(not_null<const HistoryItem*>)> callback) {
  229. _showItemCallback = std::move(callback);
  230. }
  231. void Widget::stopAndClose() {
  232. _voiceIsActive = false;
  233. if (_type == AudioMsgId::Type::Voice) {
  234. const auto songData = instance()->current(AudioMsgId::Type::Song);
  235. const auto songState = instance()->getState(AudioMsgId::Type::Song);
  236. if (songData == songState.id && !IsStoppedOrStopping(songState.state)) {
  237. instance()->stop(AudioMsgId::Type::Voice);
  238. return;
  239. }
  240. }
  241. if (_closeCallback) {
  242. _closeCallback();
  243. }
  244. }
  245. void Widget::setShadowGeometryToLeft(int x, int y, int w, int h) {
  246. _shadow->setGeometryToLeft(x, y, w, h);
  247. }
  248. void Widget::showShadowAndDropdowns() {
  249. _shadow->show();
  250. _playbackSlider->setVisible(_type == AudioMsgId::Type::Song);
  251. if (_volumeHidden) {
  252. _volumeHidden = false;
  253. _volume->show();
  254. }
  255. _speedController->showBack();
  256. _orderController->showBack();
  257. }
  258. void Widget::updateDropdownsGeometry() {
  259. const auto dropdownWidth = st::mediaPlayerVolumeSize.width();
  260. const auto position = _volume->parentWidget()->mapFromGlobal(
  261. _volumeToggle->mapToGlobal(
  262. QPoint(
  263. (_volumeToggle->width() - dropdownWidth) / 2,
  264. height())));
  265. const auto playerMargins = _volume->getMargin();
  266. const auto shift = QPoint(playerMargins.left(), playerMargins.top());
  267. _volume->move(position - shift);
  268. _orderController->updateDropdownGeometry();
  269. _speedController->updateDropdownGeometry();
  270. }
  271. void Widget::hideShadowAndDropdowns() {
  272. _shadow->hide();
  273. _playbackSlider->hide();
  274. if (!_volume->isHidden()) {
  275. _volumeHidden = true;
  276. _volume->hide();
  277. }
  278. _speedController->hideTemporarily();
  279. _orderController->hideTemporarily();
  280. }
  281. void Widget::raiseDropdowns() {
  282. _volume->raise();
  283. }
  284. Widget::~Widget() = default;
  285. not_null<Ui::RpWidget*> Widget::rightControls() {
  286. return _rightControls->entity();
  287. }
  288. void Widget::handleSeekProgress(float64 progress) {
  289. if (!_lastDurationMs) return;
  290. const auto positionMs = std::clamp(
  291. static_cast<crl::time>(progress * _lastDurationMs),
  292. crl::time(0),
  293. _lastDurationMs);
  294. if (_seekPositionMs != positionMs) {
  295. _seekPositionMs = positionMs;
  296. updateTimeLabel();
  297. instance()->startSeeking(_type);
  298. }
  299. }
  300. void Widget::handleSeekFinished(float64 progress) {
  301. if (!_lastDurationMs) return;
  302. _seekPositionMs = -1;
  303. instance()->finishSeeking(_type, progress);
  304. }
  305. void Widget::resizeEvent(QResizeEvent *e) {
  306. updateControlsGeometry();
  307. _narrow = (width() < st::mediaPlayerWideWidth);
  308. updateControlsWrapVisibility();
  309. }
  310. void Widget::updateControlsGeometry() {
  311. _close->moveToRight(st::mediaPlayerCloseRight, st::mediaPlayerPlayTop);
  312. auto right = 0;
  313. if (hasPlaybackSpeedControl()) {
  314. _speedToggle->moveToRight(right, 0); right += _speedToggle->width();
  315. }
  316. if (_type == AudioMsgId::Type::Song) {
  317. _repeatToggle->moveToRight(right, 0); right += _repeatToggle->width();
  318. _orderToggle->moveToRight(right, 0); right += _orderToggle->width();
  319. }
  320. _volumeToggle->moveToRight(right, 0); right += _volumeToggle->width();
  321. updateControlsWrapGeometry();
  322. updatePlayPrevNextPositions();
  323. _playbackSlider->setGeometry(
  324. 0,
  325. height() - st::mediaPlayerPlayback.fullWidth,
  326. width(),
  327. st::mediaPlayerPlayback.fullWidth);
  328. updateDropdownsGeometry();
  329. }
  330. void Widget::updateControlsWrapGeometry() {
  331. const auto fade = st::mediaPlayerControlsFade.width();
  332. const auto controls = getTimeRight() + _timeLabel->width() + fade;
  333. rightControls()->resize(controls, _repeatToggle->height());
  334. _rightControls->move(
  335. width() - st::mediaPlayerCloseRight - _close->width() - controls,
  336. st::mediaPlayerPlayTop);
  337. }
  338. void Widget::updateControlsWrapVisibility() {
  339. _rightControls->toggle(
  340. _over || !_narrow,
  341. isHidden() ? anim::type::instant : anim::type::normal);
  342. }
  343. void Widget::paintEvent(QPaintEvent *e) {
  344. auto p = QPainter(this);
  345. auto fill = e->rect().intersected(QRect(0, 0, width(), st::mediaPlayerHeight));
  346. if (!fill.isEmpty()) {
  347. p.fillRect(fill, st::mediaPlayerBg);
  348. }
  349. }
  350. void Widget::enterEventHook(QEnterEvent *e) {
  351. markOver(true);
  352. }
  353. void Widget::leaveEventHook(QEvent *e) {
  354. markOver(false);
  355. }
  356. void Widget::markOver(bool over) {
  357. if (over) {
  358. _over = true;
  359. _wontBeOver = false;
  360. InvokeQueued(this, [=] {
  361. updateControlsWrapVisibility();
  362. });
  363. } else {
  364. _wontBeOver = true;
  365. InvokeQueued(this, [=] {
  366. if (!_wontBeOver) {
  367. return;
  368. }
  369. _wontBeOver = false;
  370. _over = false;
  371. updateControlsWrapVisibility();
  372. });
  373. updateOverLabelsState(false);
  374. }
  375. }
  376. void Widget::saveOrder(OrderMode mode) {
  377. Core::App().settings().setPlayerOrderMode(mode);
  378. Core::App().saveSettingsDelayed();
  379. }
  380. float64 Widget::speedLookup(bool lastNonDefault) const {
  381. return Core::App().settings().voicePlaybackSpeed(lastNonDefault);
  382. }
  383. void Widget::saveSpeed(float64 speed) {
  384. Core::App().settings().setVoicePlaybackSpeed(speed);
  385. Core::App().saveSettingsDelayed();
  386. }
  387. void Widget::mouseMoveEvent(QMouseEvent *e) {
  388. updateOverLabelsState(e->pos());
  389. }
  390. void Widget::mousePressEvent(QMouseEvent *e) {
  391. _labelsDown = _labelsOver;
  392. }
  393. void Widget::mouseReleaseEvent(QMouseEvent *e) {
  394. if (auto downLabels = base::take(_labelsDown)) {
  395. if (_labelsOver != downLabels) {
  396. return;
  397. }
  398. if ((_type == AudioMsgId::Type::Voice)
  399. || _lastSongFromAnotherSession) {
  400. const auto current = instance()->current(_type);
  401. const auto document = current.audio();
  402. const auto context = current.contextId();
  403. if (document && context && _showItemCallback) {
  404. if (const auto item = document->owner().message(context)) {
  405. _showItemCallback(item);
  406. }
  407. }
  408. }
  409. }
  410. }
  411. void Widget::updateOverLabelsState(QPoint pos) {
  412. const auto left = getNameLeft();
  413. const auto right = width()
  414. - _rightControls->x()
  415. - _rightControls->width()
  416. + getTimeRight();
  417. const auto labels = myrtlrect(left, 0, width() - right - left, height() - st::mediaPlayerPlayback.fullWidth);
  418. const auto over = labels.contains(pos);
  419. updateOverLabelsState(over);
  420. }
  421. void Widget::updateOverLabelsState(bool over) {
  422. _labelsOver = over;
  423. const auto pressShowsItem = _labelsOver
  424. && ((_type == AudioMsgId::Type::Voice)
  425. || _lastSongFromAnotherSession);
  426. setCursor(pressShowsItem ? style::cur_pointer : style::cur_default);
  427. _togglePlaylistRequests.fire(over && (_type == AudioMsgId::Type::Song));
  428. }
  429. void Widget::updatePlayPrevNextPositions() {
  430. auto left = st::mediaPlayerPlayLeft;
  431. auto top = st::mediaPlayerPlayTop;
  432. if (_previousTrack) {
  433. _previousTrack->moveToLeft(left, top); left += _previousTrack->width() + st::mediaPlayerPlaySkip;
  434. _playPause->moveToLeft(left, top); left += _playPause->width() + st::mediaPlayerPlaySkip;
  435. _nextTrack->moveToLeft(left, top);
  436. } else {
  437. _playPause->moveToLeft(left, top);
  438. }
  439. updateLabelsGeometry();
  440. }
  441. int Widget::getNameLeft() const {
  442. auto result = st::mediaPlayerPlayLeft + _playPause->width();
  443. if (_previousTrack) {
  444. result += _previousTrack->width() + st::mediaPlayerPlaySkip + _nextTrack->width() + st::mediaPlayerPlaySkip;
  445. }
  446. result += st::mediaPlayerPadding;
  447. return result;
  448. }
  449. int Widget::getNameRight() const {
  450. return st::mediaPlayerCloseRight
  451. + _close->width()
  452. + st::mediaPlayerPadding;
  453. }
  454. int Widget::getTimeRight() const {
  455. auto result = 0;
  456. result += _volumeToggle->width();
  457. if (_type == AudioMsgId::Type::Song) {
  458. result += _repeatToggle->width()
  459. + _orderToggle->width();
  460. }
  461. if (hasPlaybackSpeedControl()) {
  462. result += _speedToggle->width();
  463. }
  464. result += st::mediaPlayerPadding;
  465. return result;
  466. }
  467. void Widget::updateLabelsGeometry() {
  468. const auto left = getNameLeft();
  469. const auto widthForName = width()
  470. - left
  471. - getNameRight();
  472. _nameLabel->resizeToNaturalWidth(widthForName);
  473. _nameLabel->moveToLeft(left, st::mediaPlayerNameTop - st::mediaPlayerName.style.font->ascent);
  474. const auto right = getTimeRight();
  475. _timeLabel->moveToRight(right, st::mediaPlayerNameTop - st::mediaPlayerTime.font->ascent);
  476. updateControlsWrapGeometry();
  477. }
  478. void Widget::updateRepeatToggleIcon() {
  479. switch (Core::App().settings().playerRepeatMode()) {
  480. case RepeatMode::None:
  481. _repeatToggle->setIconOverride(
  482. &st::mediaPlayerRepeatDisabledIcon,
  483. &st::mediaPlayerRepeatDisabledIconOver);
  484. _repeatToggle->setRippleColorOverride(
  485. &st::mediaPlayerRepeatDisabledRippleBg);
  486. break;
  487. case RepeatMode::One:
  488. _repeatToggle->setIconOverride(&st::mediaPlayerRepeatOneIcon);
  489. _repeatToggle->setRippleColorOverride(nullptr);
  490. break;
  491. case RepeatMode::All:
  492. _repeatToggle->setIconOverride(nullptr);
  493. _repeatToggle->setRippleColorOverride(nullptr);
  494. break;
  495. }
  496. }
  497. void Widget::checkForTypeChange() {
  498. auto hasActiveType = [](AudioMsgId::Type type) {
  499. const auto current = instance()->current(type);
  500. const auto state = instance()->getState(type);
  501. return (current == state.id && !IsStoppedOrStopping(state.state));
  502. };
  503. if (hasActiveType(AudioMsgId::Type::Voice)) {
  504. _voiceIsActive = true;
  505. setType(AudioMsgId::Type::Voice);
  506. } else if (!_voiceIsActive && hasActiveType(AudioMsgId::Type::Song)) {
  507. setType(AudioMsgId::Type::Song);
  508. }
  509. }
  510. bool Widget::hasPlaybackSpeedControl() const {
  511. return _lastSongId.changeablePlaybackSpeed()
  512. && Media::Audio::SupportsSpeedControl();
  513. }
  514. void Widget::updateControlsVisibility() {
  515. _repeatToggle->setVisible(_type == AudioMsgId::Type::Song);
  516. _orderToggle->setVisible(_type == AudioMsgId::Type::Song);
  517. _speedToggle->setVisible(hasPlaybackSpeedControl());
  518. if (!_shadow->isHidden()) {
  519. _playbackSlider->setVisible(_type == AudioMsgId::Type::Song);
  520. }
  521. updateControlsGeometry();
  522. }
  523. void Widget::setType(AudioMsgId::Type type) {
  524. if (_type != type) {
  525. _type = type;
  526. handleSongChange();
  527. updateControlsVisibility();
  528. updateLabelsGeometry();
  529. handleSongUpdate(instance()->getState(_type));
  530. updateOverLabelsState(_labelsOver);
  531. _playlistChangesLifetime = instance()->playlistChanges(
  532. _type
  533. ) | rpl::start_with_next([=] {
  534. handlePlaylistUpdate();
  535. });
  536. // maybe the type change causes a change of the button layout
  537. QResizeEvent event = { size(), size() };
  538. resizeEvent(&event);
  539. }
  540. }
  541. void Widget::handleSongUpdate(const TrackState &state) {
  542. checkForTypeChange();
  543. if (state.id.type() != _type || !state.id.audio()) {
  544. return;
  545. }
  546. if (state.id.audio()->loading()) {
  547. _playbackProgress->updateLoadingState(state.id.audio()->progress());
  548. } else {
  549. _playbackProgress->updateState(state);
  550. }
  551. auto showPause = ShowPauseIcon(state.state);
  552. if (instance()->isSeeking(_type)) {
  553. showPause = true;
  554. }
  555. _playPause->setIconOverride(state.id.audio()->loading()
  556. ? &st::mediaPlayerCancelIcon
  557. : showPause
  558. ? &st::mediaPlayerPauseIcon
  559. : nullptr);
  560. updateTimeText(state);
  561. }
  562. void Widget::updateTimeText(const TrackState &state) {
  563. qint64 display = 0;
  564. const auto frequency = state.frequency;
  565. const auto document = state.id.audio();
  566. if (!IsStoppedOrStopping(state.state)) {
  567. display = state.position;
  568. } else if (state.length) {
  569. display = state.length;
  570. } else if (const auto song = document->song()) {
  571. display = (document->duration() * frequency) / 1000;
  572. }
  573. _lastDurationMs = (state.length * 1000LL) / frequency;
  574. if (document->loading()) {
  575. _time = QString::number(qRound(document->progress() * 100)) + '%';
  576. _playbackSlider->setDisabled(true);
  577. } else {
  578. display = display / frequency;
  579. _time = Ui::FormatDurationText(display);
  580. _playbackSlider->setDisabled(false);
  581. }
  582. if (_seekPositionMs < 0) {
  583. updateTimeLabel();
  584. }
  585. }
  586. void Widget::updateTimeLabel() {
  587. auto timeLabelWidth = _timeLabel->width();
  588. if (_seekPositionMs >= 0) {
  589. auto playAlready = _seekPositionMs / 1000LL;
  590. _timeLabel->setText(Ui::FormatDurationText(playAlready));
  591. } else {
  592. _timeLabel->setText(_time);
  593. }
  594. if (timeLabelWidth != _timeLabel->width()) {
  595. updateLabelsGeometry();
  596. }
  597. }
  598. void Widget::handleSongChange() {
  599. const auto current = instance()->current(_type);
  600. const auto document = current.audio();
  601. _lastSongFromAnotherSession = document
  602. && (document->session().uniqueId()
  603. != _controller->session().uniqueId());
  604. if (!current
  605. || !document
  606. || ((_lastSongId.audio() == document)
  607. && (_lastSongId.contextId() == current.contextId()))) {
  608. return;
  609. }
  610. _lastSongId = current;
  611. auto textWithEntities = TextWithEntities();
  612. if (document->isVoiceMessage() || document->isVideoMessage()) {
  613. if (const auto item = document->owner().message(current.contextId())) {
  614. const auto name = (!item->out() || item->isPost())
  615. ? item->fromOriginal()->name()
  616. : tr::lng_from_you(tr::now);
  617. const auto date = [item] {
  618. const auto parsed = ItemDateTime(item);
  619. const auto date = parsed.date();
  620. const auto time = QLocale().toString(parsed.time(), QLocale::ShortFormat);
  621. const auto today = QDateTime::currentDateTime().date();
  622. if (date == today) {
  623. return tr::lng_player_message_today(
  624. tr::now,
  625. lt_time,
  626. time);
  627. } else if (date.addDays(1) == today) {
  628. return tr::lng_player_message_yesterday(
  629. tr::now,
  630. lt_time,
  631. time);
  632. }
  633. return tr::lng_player_message_date(
  634. tr::now,
  635. lt_date,
  636. langDayOfMonthFull(date),
  637. lt_time,
  638. time);
  639. };
  640. textWithEntities.text = name + ' ' + date();
  641. textWithEntities.entities.append(EntityInText(
  642. EntityType::Semibold,
  643. 0,
  644. name.size(),
  645. QString()));
  646. } else if (document->isVideoMessage()) {
  647. textWithEntities.text = tr::lng_media_round(tr::now);
  648. } else {
  649. textWithEntities.text = tr::lng_media_audio(tr::now);
  650. }
  651. } else {
  652. textWithEntities = Ui::Text::FormatSongNameFor(document)
  653. .textWithEntities(true);
  654. }
  655. _nameLabel->setMarkedText(textWithEntities);
  656. handlePlaylistUpdate();
  657. updateLabelsGeometry();
  658. }
  659. void Widget::handlePlaylistUpdate() {
  660. const auto previousEnabled = instance()->previousAvailable(_type);
  661. const auto nextEnabled = instance()->nextAvailable(_type);
  662. if (!previousEnabled && !nextEnabled) {
  663. destroyPrevNextButtons();
  664. } else {
  665. createPrevNextButtons();
  666. _previousTrack->setIconOverride(previousEnabled ? nullptr : &st::mediaPlayerPreviousDisabledIcon);
  667. _previousTrack->setRippleColorOverride(previousEnabled ? nullptr : &st::mediaPlayerBg);
  668. _previousTrack->setPointerCursor(previousEnabled);
  669. _nextTrack->setIconOverride(nextEnabled ? nullptr : &st::mediaPlayerNextDisabledIcon);
  670. _nextTrack->setRippleColorOverride(nextEnabled ? nullptr : &st::mediaPlayerBg);
  671. _nextTrack->setPointerCursor(nextEnabled);
  672. }
  673. }
  674. void Widget::createPrevNextButtons() {
  675. if (!_previousTrack) {
  676. _previousTrack.create(this, st::mediaPlayerPreviousButton);
  677. _previousTrack->show();
  678. _previousTrack->setClickedCallback([=]() {
  679. instance()->previous(_type);
  680. });
  681. _nextTrack.create(this, st::mediaPlayerNextButton);
  682. _nextTrack->show();
  683. _nextTrack->setClickedCallback([=]() {
  684. instance()->next(_type);
  685. });
  686. hidePlaylistOn(_previousTrack);
  687. hidePlaylistOn(_nextTrack);
  688. updatePlayPrevNextPositions();
  689. }
  690. }
  691. void Widget::destroyPrevNextButtons() {
  692. if (_previousTrack) {
  693. _previousTrack.destroy();
  694. _nextTrack.destroy();
  695. updatePlayPrevNextPositions();
  696. }
  697. }
  698. } // namespace Player
  699. } // namespace Media