355 lines
8.9 KiB
C++
355 lines
8.9 KiB
C++
/*
|
|
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 "info/saved/info_saved_music_widget.h"
|
|
|
|
#include "data/data_document.h"
|
|
#include "data/data_peer.h"
|
|
#include "data/data_saved_music.h"
|
|
#include "data/data_session.h"
|
|
#include "info/media/info_media_list_widget.h"
|
|
#include "info/info_controller.h"
|
|
#include "info/info_memento.h"
|
|
#include "main/main_session.h"
|
|
#include "ui/text/text_utilities.h"
|
|
#include "ui/widgets/buttons.h"
|
|
#include "ui/widgets/labels.h"
|
|
#include "ui/widgets/scroll_area.h"
|
|
#include "ui/wrap/slide_wrap.h"
|
|
#include "ui/wrap/vertical_layout.h"
|
|
#include "lang/lang_keys.h"
|
|
#include "ui/ui_utility.h"
|
|
#include "styles/style_credits.h" // giftListAbout
|
|
#include "styles/style_info.h"
|
|
#include "styles/style_layers.h"
|
|
|
|
namespace Info::Saved {
|
|
|
|
class MusicInner final : public Ui::RpWidget {
|
|
public:
|
|
MusicInner(QWidget *parent, not_null<Controller*> controller);
|
|
~MusicInner();
|
|
|
|
bool showInternal(not_null<MusicMemento*> memento);
|
|
void setIsStackBottom(bool isStackBottom) {
|
|
_isStackBottom = isStackBottom;
|
|
}
|
|
|
|
void saveState(not_null<MusicMemento*> memento);
|
|
void restoreState(not_null<MusicMemento*> memento);
|
|
|
|
void setScrollHeightValue(rpl::producer<int> value);
|
|
|
|
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const;
|
|
rpl::producer<SelectedItems> selectedListValue() const;
|
|
void selectionAction(SelectionAction action);
|
|
|
|
protected:
|
|
int resizeGetHeight(int newWidth) override;
|
|
void visibleTopBottomUpdated(
|
|
int visibleTop,
|
|
int visibleBottom) override;
|
|
|
|
private:
|
|
int recountHeight();
|
|
void refreshHeight();
|
|
void refreshEmpty();
|
|
|
|
void setupList();
|
|
void setupEmpty();
|
|
|
|
const not_null<Controller*> _controller;
|
|
const not_null<PeerData*> _peer;
|
|
|
|
base::unique_qptr<Ui::PopupMenu> _menu;
|
|
|
|
object_ptr<Media::ListWidget> _list = { nullptr };
|
|
object_ptr<Ui::RpWidget> _empty = { nullptr };
|
|
int _lastNonLoadingHeight = 0;
|
|
bool _emptyLoading = false;
|
|
|
|
bool _inResize = false;
|
|
bool _isStackBottom = false;
|
|
|
|
rpl::event_stream<Ui::ScrollToRequest> _scrollToRequests;
|
|
rpl::event_stream<rpl::producer<SelectedItems>> _selectedLists;
|
|
rpl::event_stream<rpl::producer<int>> _listTops;
|
|
rpl::variable<int> _topHeight;
|
|
rpl::variable<bool> _albumEmpty;
|
|
|
|
};
|
|
|
|
MusicInner::MusicInner(QWidget *parent, not_null<Controller*> controller)
|
|
: RpWidget(parent)
|
|
, _controller(controller)
|
|
, _peer(controller->key().musicPeer()) {
|
|
setupList();
|
|
setupEmpty();
|
|
}
|
|
|
|
MusicInner::~MusicInner() = default;
|
|
|
|
void MusicInner::visibleTopBottomUpdated(
|
|
int visibleTop,
|
|
int visibleBottom) {
|
|
setChildVisibleTopBottom(_list, visibleTop, visibleBottom);
|
|
}
|
|
|
|
bool MusicInner::showInternal(not_null<MusicMemento*> memento) {
|
|
if (memento->section().type() == Section::Type::SavedMusic) {
|
|
restoreState(memento);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void MusicInner::setupList() {
|
|
Expects(!_list);
|
|
|
|
_list = object_ptr<Media::ListWidget>(this, _controller);
|
|
if (_peer->isSelf()) {
|
|
_list->setReorderDescriptor({
|
|
.save = [=](
|
|
int oldPosition,
|
|
int newPosition,
|
|
Fn<void()> done,
|
|
Fn<void()> fail) {
|
|
_controller->session().data().savedMusic().reorder(
|
|
oldPosition,
|
|
newPosition);
|
|
done();
|
|
}
|
|
});
|
|
}
|
|
const auto raw = _list.data();
|
|
|
|
using namespace rpl::mappers;
|
|
raw->scrollToRequests(
|
|
) | rpl::map([=](int to) {
|
|
return Ui::ScrollToRequest {
|
|
raw->y() + to,
|
|
-1
|
|
};
|
|
}) | rpl::start_to_stream(_scrollToRequests, raw->lifetime());
|
|
_selectedLists.fire(raw->selectedListValue());
|
|
_listTops.fire(raw->topValue());
|
|
|
|
raw->show();
|
|
}
|
|
|
|
void MusicInner::setupEmpty() {
|
|
_list->resizeToWidth(width());
|
|
|
|
const auto savedMusic = &_controller->session().data().savedMusic();
|
|
rpl::combine(
|
|
rpl::single(
|
|
rpl::empty
|
|
) | rpl::then(
|
|
savedMusic->changed() | rpl::filter(
|
|
rpl::mappers::_1 == _peer->id
|
|
) | rpl::to_empty
|
|
),
|
|
_list->heightValue()
|
|
) | rpl::on_next([=](auto, int listHeight) {
|
|
const auto padding = st::infoMediaMargin;
|
|
if (const auto raw = _empty.release()) {
|
|
raw->hide();
|
|
raw->deleteLater();
|
|
}
|
|
_emptyLoading = false;
|
|
if (listHeight <= padding.bottom() + padding.top()) {
|
|
refreshEmpty();
|
|
} else {
|
|
_albumEmpty = false;
|
|
}
|
|
refreshHeight();
|
|
}, _list->lifetime());
|
|
}
|
|
|
|
void MusicInner::refreshEmpty() {
|
|
const auto savedMusic = &_controller->session().data().savedMusic();
|
|
const auto knownEmpty = savedMusic->countKnown(_peer->id);
|
|
_empty = object_ptr<Ui::FlatLabel>(
|
|
this,
|
|
(!knownEmpty
|
|
? tr::lng_contacts_loading(tr::marked)
|
|
: rpl::single(
|
|
tr::lng_media_song_empty(tr::now, tr::marked))),
|
|
st::giftListAbout);
|
|
_empty->show();
|
|
_emptyLoading = !knownEmpty;
|
|
resizeToWidth(width());
|
|
}
|
|
|
|
void MusicInner::saveState(not_null<MusicMemento*> memento) {
|
|
_list->saveState(&memento->media());
|
|
}
|
|
|
|
void MusicInner::restoreState(not_null<MusicMemento*> memento) {
|
|
_list->restoreState(&memento->media());
|
|
}
|
|
|
|
rpl::producer<SelectedItems> MusicInner::selectedListValue() const {
|
|
return _selectedLists.events_starting_with(
|
|
_list->selectedListValue()
|
|
) | rpl::flatten_latest();
|
|
}
|
|
|
|
void MusicInner::selectionAction(SelectionAction action) {
|
|
_list->selectionAction(action);
|
|
}
|
|
|
|
int MusicInner::resizeGetHeight(int newWidth) {
|
|
if (!newWidth) {
|
|
return 0;
|
|
}
|
|
_inResize = true;
|
|
auto guard = gsl::finally([this] { _inResize = false; });
|
|
|
|
if (_list) {
|
|
_list->resizeToWidth(newWidth);
|
|
}
|
|
if (const auto empty = _empty.get()) {
|
|
const auto margin = st::giftListAboutMargin;
|
|
empty->resizeToWidth(newWidth - margin.left() - margin.right());
|
|
}
|
|
|
|
return recountHeight();
|
|
}
|
|
|
|
void MusicInner::refreshHeight() {
|
|
if (_inResize) {
|
|
return;
|
|
}
|
|
resize(width(), recountHeight());
|
|
}
|
|
|
|
int MusicInner::recountHeight() {
|
|
auto top = 0;
|
|
auto listHeight = 0;
|
|
if (_list) {
|
|
_list->moveToLeft(0, top);
|
|
listHeight = _list->heightNoMargins();
|
|
top += listHeight;
|
|
}
|
|
if (const auto empty = _empty.get()) {
|
|
const auto margin = st::giftListAboutMargin;
|
|
empty->moveToLeft(margin.left(), top + margin.top());
|
|
top += margin.top() + empty->height() + margin.bottom();
|
|
}
|
|
if (_emptyLoading) {
|
|
top = std::max(top, _lastNonLoadingHeight);
|
|
} else {
|
|
_lastNonLoadingHeight = top;
|
|
}
|
|
return top;
|
|
}
|
|
|
|
void MusicInner::setScrollHeightValue(rpl::producer<int> value) {
|
|
}
|
|
|
|
rpl::producer<Ui::ScrollToRequest> MusicInner::scrollToRequests() const {
|
|
return _scrollToRequests.events();
|
|
}
|
|
|
|
MusicMemento::MusicMemento(not_null<Controller*> controller)
|
|
: ContentMemento(MusicTag{ controller->musicPeer() })
|
|
, _media(controller) {
|
|
}
|
|
|
|
MusicMemento::MusicMemento(not_null<PeerData*> peer)
|
|
: ContentMemento(MusicTag{ peer })
|
|
, _media(peer, 0, Media::Type::MusicFile) {
|
|
}
|
|
|
|
MusicMemento::~MusicMemento() = default;
|
|
|
|
Section MusicMemento::section() const {
|
|
return Section(Section::Type::SavedMusic);
|
|
}
|
|
|
|
object_ptr<ContentWidget> MusicMemento::createWidget(
|
|
QWidget *parent,
|
|
not_null<Controller*> controller,
|
|
const QRect &geometry) {
|
|
auto result = object_ptr<MusicWidget>(parent, controller);
|
|
result->setInternalState(geometry, this);
|
|
return result;
|
|
}
|
|
|
|
MusicWidget::MusicWidget(
|
|
QWidget *parent,
|
|
not_null<Controller*> controller)
|
|
: ContentWidget(parent, controller) {
|
|
_inner = setInnerWidget(object_ptr<MusicInner>(this, controller));
|
|
_inner->setScrollHeightValue(scrollHeightValue());
|
|
_inner->scrollToRequests(
|
|
) | rpl::on_next([this](Ui::ScrollToRequest request) {
|
|
scrollTo(request);
|
|
}, _inner->lifetime());
|
|
}
|
|
|
|
void MusicWidget::setIsStackBottom(bool isStackBottom) {
|
|
ContentWidget::setIsStackBottom(isStackBottom);
|
|
_inner->setIsStackBottom(isStackBottom);
|
|
}
|
|
|
|
bool MusicWidget::showInternal(not_null<ContentMemento*> memento) {
|
|
if (!controller()->validateMementoPeer(memento)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void MusicWidget::setInternalState(
|
|
const QRect &geometry,
|
|
not_null<MusicMemento*> memento) {
|
|
setGeometry(geometry);
|
|
Ui::SendPendingMoveResizeEvents(this);
|
|
restoreState(memento);
|
|
}
|
|
|
|
std::shared_ptr<ContentMemento> MusicWidget::doCreateMemento() {
|
|
auto result = std::make_shared<MusicMemento>(controller());
|
|
saveState(result.get());
|
|
return result;
|
|
}
|
|
|
|
void MusicWidget::saveState(not_null<MusicMemento*> memento) {
|
|
memento->setScrollTop(scrollTopSave());
|
|
_inner->saveState(memento);
|
|
}
|
|
|
|
void MusicWidget::restoreState(not_null<MusicMemento*> memento) {
|
|
_inner->restoreState(memento);
|
|
scrollTopRestore(memento->scrollTop());
|
|
}
|
|
|
|
rpl::producer<SelectedItems> MusicWidget::selectedListValue() const {
|
|
return _inner->selectedListValue();
|
|
}
|
|
|
|
void MusicWidget::selectionAction(SelectionAction action) {
|
|
_inner->selectionAction(action);
|
|
}
|
|
|
|
rpl::producer<QString> MusicWidget::title() {
|
|
return controller()->key().musicPeer()->isSelf()
|
|
? tr::lng_media_saved_music_your()
|
|
: tr::lng_media_saved_music_title();
|
|
}
|
|
|
|
std::shared_ptr<Info::Memento> MakeMusic(not_null<PeerData*> peer) {
|
|
return std::make_shared<Info::Memento>(
|
|
std::vector<std::shared_ptr<ContentMemento>>(
|
|
1,
|
|
std::make_shared<MusicMemento>(peer)));
|
|
}
|
|
|
|
} // namespace Info::Saved
|
|
|