| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417 |
- /*
- 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 "media/player/media_player_panel.h"
- #include "media/player/media_player_instance.h"
- #include "info/media/info_media_list_widget.h"
- #include "history/history.h"
- #include "history/history_item.h"
- #include "data/data_session.h"
- #include "data/data_document.h"
- #include "data/data_channel.h"
- #include "data/data_chat.h"
- #include "ui/widgets/shadow.h"
- #include "ui/widgets/scroll_area.h"
- #include "ui/cached_round_corners.h"
- #include "ui/ui_utility.h"
- #include "mainwindow.h"
- #include "main/main_session.h"
- #include "styles/style_overview.h"
- #include "styles/style_media_player.h"
- #include "styles/style_info.h"
- namespace Media {
- namespace Player {
- namespace {
- using ListWidget = Info::Media::ListWidget;
- constexpr auto kPlaylistIdsLimit = 32;
- constexpr auto kDelayedHideTimeout = crl::time(3000);
- } // namespace
- Panel::Panel(
- QWidget *parent,
- not_null<Window::SessionController*> window)
- : RpWidget(parent)
- , AbstractController(window)
- , _showTimer([=] { startShow(); })
- , _hideTimer([=] { startHideChecked(); })
- , _scroll(this, st::mediaPlayerScroll) {
- hide();
- updateSize();
- }
- bool Panel::overlaps(const QRect &globalRect) {
- if (isHidden() || _a_appearance.animating()) return false;
- auto marginLeft = rtl() ? contentRight() : contentLeft();
- auto marginRight = rtl() ? contentLeft() : contentRight();
- return rect().marginsRemoved(QMargins(marginLeft, contentTop(), marginRight, contentBottom())).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size()));
- }
- void Panel::resizeEvent(QResizeEvent *e) {
- updateControlsGeometry();
- }
- void Panel::listHeightUpdated(int newHeight) {
- if (newHeight > emptyInnerHeight()) {
- updateSize();
- } else {
- _hideTimer.callOnce(0);
- }
- }
- bool Panel::contentTooSmall() const {
- const auto innerHeight = _scroll->widget()
- ? _scroll->widget()->height()
- : emptyInnerHeight();
- return (innerHeight <= emptyInnerHeight());
- }
- int Panel::emptyInnerHeight() const {
- return st::infoMediaMargin.top()
- + st::overviewFileLayout.songPadding.top()
- + st::overviewFileLayout.songThumbSize
- + st::overviewFileLayout.songPadding.bottom()
- + st::infoMediaMargin.bottom();
- }
- bool Panel::preventAutoHide() const {
- if (const auto list = static_cast<ListWidget*>(_scroll->widget())) {
- return list->preventAutoHide();
- }
- return false;
- }
- void Panel::updateControlsGeometry() {
- const auto scrollTop = contentTop();
- const auto width = contentWidth();
- const auto scrollHeight = qMax(
- height() - scrollTop - contentBottom() - scrollMarginBottom(),
- 0);
- if (scrollHeight > 0) {
- _scroll->setGeometryToRight(contentRight(), scrollTop, width, scrollHeight);
- }
- if (const auto widget = static_cast<TWidget*>(_scroll->widget())) {
- widget->resizeToWidth(width);
- }
- }
- int Panel::bestPositionFor(int left) const {
- left -= contentLeft();
- left -= st::mediaPlayerFileLayout.songPadding.left();
- left -= st::mediaPlayerFileLayout.songThumbSize / 2;
- return left;
- }
- void Panel::scrollPlaylistToCurrentTrack() {
- if (const auto list = static_cast<ListWidget*>(_scroll->widget())) {
- const auto rect = list->getCurrentSongGeometry();
- _scroll->scrollToY(rect.y() - st::infoMediaMargin.top());
- }
- }
- void Panel::updateSize() {
- auto width = contentLeft() + st::mediaPlayerPanelWidth + contentRight();
- auto height = contentTop();
- auto listHeight = 0;
- if (auto widget = _scroll->widget()) {
- listHeight = widget->height();
- }
- auto scrollVisible = (listHeight > 0);
- auto scrollHeight = scrollVisible ? (qMin(listHeight, st::mediaPlayerListHeightMax) + st::mediaPlayerListMarginBottom) : 0;
- height += scrollHeight + contentBottom();
- resize(width, height);
- _scroll->setVisible(scrollVisible);
- }
- void Panel::paintEvent(QPaintEvent *e) {
- auto p = QPainter(this);
- if (!_cache.isNull()) {
- bool animating = _a_appearance.animating();
- if (animating) {
- p.setOpacity(_a_appearance.value(_hiding ? 0. : 1.));
- } else if (_hiding || isHidden()) {
- hideFinished();
- return;
- }
- p.drawPixmap(0, 0, _cache);
- if (!animating) {
- showChildren();
- _cache = QPixmap();
- }
- return;
- }
- // draw shadow
- auto shadowedRect = myrtlrect(contentLeft(), contentTop(), contentWidth(), contentHeight());
- auto shadowedSides = (rtl() ? RectPart::Right : RectPart::Left)
- | RectPart::Bottom
- | (rtl() ? RectPart::Left : RectPart::Right)
- | RectPart::Top;
- Ui::Shadow::paint(p, shadowedRect, width(), st::defaultRoundShadow, shadowedSides);
- Ui::FillRoundRect(p, shadowedRect, st::menuBg, Ui::MenuCorners);
- }
- void Panel::enterEventHook(QEnterEvent *e) {
- if (_ignoringEnterEvents || contentTooSmall()) return;
- _hideTimer.cancel();
- if (_a_appearance.animating()) {
- startShow();
- } else {
- _showTimer.callOnce(0);
- }
- return RpWidget::enterEventHook(e);
- }
- void Panel::leaveEventHook(QEvent *e) {
- if (preventAutoHide()) {
- return;
- }
- _showTimer.cancel();
- if (_a_appearance.animating()) {
- startHide();
- } else {
- _hideTimer.callOnce(300);
- }
- return RpWidget::leaveEventHook(e);
- }
- void Panel::showFromOther() {
- _hideTimer.cancel();
- if (_a_appearance.animating()) {
- startShow();
- } else {
- _showTimer.callOnce(300);
- }
- }
- void Panel::hideFromOther() {
- _showTimer.cancel();
- if (_a_appearance.animating()) {
- startHide();
- } else {
- _hideTimer.callOnce(0);
- }
- }
- void Panel::ensureCreated() {
- if (_scroll->widget()) return;
- _refreshListLifetime = instance()->playlistChanges(
- AudioMsgId::Type::Song
- ) | rpl::start_with_next([=] {
- refreshList();
- });
- refreshList();
- macWindowDeactivateEvents(
- ) | rpl::filter([=] {
- return !isHidden();
- }) | rpl::start_with_next([=] {
- leaveEvent(nullptr);
- }, _refreshListLifetime);
- _ignoringEnterEvents = false;
- }
- void Panel::refreshList() {
- const auto current = instance()->current(AudioMsgId::Type::Song);
- const auto contextId = current.contextId();
- const auto peer = [&]() -> PeerData* {
- if (const auto document = current.audio()) {
- if (&document->session() != &session()) {
- // Different account is playing music.
- return nullptr;
- }
- }
- const auto item = contextId
- ? session().data().message(contextId)
- : nullptr;
- const auto media = item ? item->media() : nullptr;
- const auto document = media ? media->document() : nullptr;
- if (!document
- || !document->isSharedMediaMusic()
- || (!item->isRegular() && !item->isScheduled())) {
- return nullptr;
- }
- const auto result = item->history()->peer;
- if (const auto migrated = result->migrateTo()) {
- return migrated;
- }
- return result;
- }();
- const auto migrated = peer ? peer->migrateFrom() : nullptr;
- if (_listPeer != peer || _listMigratedPeer != migrated) {
- _scroll->takeWidget<QWidget>().destroy();
- _listPeer = _listMigratedPeer = nullptr;
- }
- if (peer && !_listPeer) {
- _listPeer = peer;
- _listMigratedPeer = migrated;
- auto list = object_ptr<ListWidget>(this, infoController());
- const auto weak = _scroll->setOwnedWidget(std::move(list));
- updateSize();
- updateControlsGeometry();
- weak->checkForHide(
- ) | rpl::start_with_next([this] {
- if (!rect().contains(mapFromGlobal(QCursor::pos()))) {
- _hideTimer.callOnce(kDelayedHideTimeout);
- }
- }, weak->lifetime());
- weak->heightValue(
- ) | rpl::start_with_next([this](int newHeight) {
- listHeightUpdated(newHeight);
- }, weak->lifetime());
- weak->scrollToRequests(
- ) | rpl::start_with_next([this](int newScrollTop) {
- _scroll->scrollToY(newScrollTop);
- }, weak->lifetime());
- // MSVC BUG + REGRESSION rpl::mappers::tuple :(
- using namespace rpl::mappers;
- rpl::combine(
- _scroll->scrollTopValue(),
- _scroll->heightValue()
- ) | rpl::start_with_next([=](int top, int height) {
- const auto bottom = top + height;
- weak->setVisibleTopBottom(top, bottom);
- }, weak->lifetime());
- auto memento = Info::Media::Memento(
- peer,
- migratedPeerId(),
- section().mediaType());
- memento.setAroundId(contextId);
- memento.setIdsLimit(kPlaylistIdsLimit);
- memento.setScrollTopItem({ contextId, peer->session().uniqueId() });
- memento.setScrollTopShift(-st::infoMediaMargin.top());
- weak->restoreState(&memento);
- }
- }
- void Panel::performDestroy() {
- if (!_scroll->widget()) return;
- _scroll->takeWidget<QWidget>().destroy();
- _listPeer = _listMigratedPeer = nullptr;
- _refreshListLifetime.destroy();
- }
- Info::Key Panel::key() const {
- return Info::Key(_listPeer);
- }
- PeerData *Panel::migrated() const {
- return _listMigratedPeer;
- }
- Info::Section Panel::section() const {
- return Info::Section(Info::Section::MediaType::MusicFile);
- }
- void Panel::startShow() {
- ensureCreated();
- if (contentTooSmall()) {
- return;
- }
- if (isHidden()) {
- scrollPlaylistToCurrentTrack();
- show();
- } else if (!_hiding) {
- return;
- }
- _hiding = false;
- startAnimation();
- }
- void Panel::hideIgnoringEnterEvents() {
- _ignoringEnterEvents = true;
- if (isHidden()) {
- hideFinished();
- } else {
- startHide();
- }
- }
- void Panel::startHideChecked() {
- if (!contentTooSmall() && preventAutoHide()) {
- return;
- }
- if (isHidden()) {
- hideFinished();
- } else {
- startHide();
- }
- }
- void Panel::startHide() {
- if (_hiding || isHidden()) return;
- _hiding = true;
- startAnimation();
- }
- void Panel::startAnimation() {
- auto from = _hiding ? 1. : 0.;
- auto to = _hiding ? 0. : 1.;
- if (_cache.isNull()) {
- showChildren();
- _cache = Ui::GrabWidget(this);
- }
- hideChildren();
- _a_appearance.start([this] { appearanceCallback(); }, from, to, st::defaultInnerDropdown.duration);
- }
- void Panel::appearanceCallback() {
- if (!_a_appearance.animating() && _hiding) {
- _hiding = false;
- hideFinished();
- } else {
- update();
- }
- }
- void Panel::hideFinished() {
- hide();
- _cache = QPixmap();
- performDestroy();
- }
- int Panel::contentLeft() const {
- return st::mediaPlayerPanelMarginLeft;
- }
- int Panel::contentTop() const {
- return st::mediaPlayerPanelMarginLeft;
- }
- int Panel::contentRight() const {
- return st::mediaPlayerPanelMarginLeft;
- }
- int Panel::contentBottom() const {
- return st::mediaPlayerPanelMarginBottom;
- }
- int Panel::scrollMarginBottom() const {
- return 0;// st::mediaPlayerPanelMarginBottom;
- }
- } // namespace Player
- } // namespace Media
|