mirror of https://github.com/procxx/kepka.git
Show info media overview using Overview::Layout.
This commit is contained in:
parent
7905694b31
commit
ecbc0ae57e
|
@ -510,9 +510,9 @@ SharedMediaMergedSlice::SharedMediaMergedSlice(
|
||||||
Key key,
|
Key key,
|
||||||
SharedMediaSlice part,
|
SharedMediaSlice part,
|
||||||
base::optional<SharedMediaSlice> migrated)
|
base::optional<SharedMediaSlice> migrated)
|
||||||
: _key(key)
|
: _key(key)
|
||||||
, _part(std::move(part))
|
, _part(std::move(part))
|
||||||
, _migrated(std::move(migrated)) {
|
, _migrated(std::move(migrated)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
base::optional<int> SharedMediaMergedSlice::fullCount() const {
|
base::optional<int> SharedMediaMergedSlice::fullCount() const {
|
||||||
|
|
|
@ -32,6 +32,8 @@ InfoToggle {
|
||||||
rippleAreaPadding: pixels;
|
rippleAreaPadding: pixels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
infoMediaHeaderFg: windowFg;
|
||||||
|
|
||||||
infoToggleCheckbox: Checkbox(defaultCheckbox) {
|
infoToggleCheckbox: Checkbox(defaultCheckbox) {
|
||||||
margin: margins(0px, 0px, 0px, 0px);
|
margin: margins(0px, 0px, 0px, 0px);
|
||||||
rippleBgActive: windowBgOver;
|
rippleBgActive: windowBgOver;
|
||||||
|
@ -113,7 +115,7 @@ infoLayerTopBar: InfoTopBar {
|
||||||
bg: boxBg;
|
bg: boxBg;
|
||||||
}
|
}
|
||||||
|
|
||||||
infoMinimalWidth: 320px;
|
infoMinimalWidth: 324px;
|
||||||
infoDesiredWidth: 360px;
|
infoDesiredWidth: 360px;
|
||||||
infoMinimalLayerMargin: 48px;
|
infoMinimalLayerMargin: 48px;
|
||||||
|
|
||||||
|
@ -342,3 +344,11 @@ infoMembersCancelSearch: CrossButton {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
infoMembersSearchTop: 15px;
|
infoMembersSearchTop: 15px;
|
||||||
|
|
||||||
|
infoMediaHeaderStyle: TextStyle(semiboldTextStyle) {
|
||||||
|
}
|
||||||
|
infoMediaHeaderHeight: 28px;
|
||||||
|
infoMediaHeaderPosition: point(14px, 6px);
|
||||||
|
infoMediaSkip: 6px;
|
||||||
|
infoMediaMargin: margins(0px, 6px, 0px, 2px);
|
||||||
|
infoMediaMinGridSize: minPhotoSize;
|
||||||
|
|
|
@ -79,6 +79,9 @@ void WrapWidget::createTabs() {
|
||||||
sections.push_back(lang(lng_profile_info_section).toUpper());
|
sections.push_back(lang(lng_profile_info_section).toUpper());
|
||||||
sections.push_back(lang(lng_info_tab_media).toUpper());
|
sections.push_back(lang(lng_info_tab_media).toUpper());
|
||||||
_topTabs->setSections(sections);
|
_topTabs->setSections(sections);
|
||||||
|
_topTabs->setActiveSection(static_cast<int>(_tab));
|
||||||
|
_topTabs->finishAnimating();
|
||||||
|
|
||||||
_topTabs->sectionActivated()
|
_topTabs->sectionActivated()
|
||||||
| rpl::map([](int index) { return static_cast<Tab>(index); })
|
| rpl::map([](int index) { return static_cast<Tab>(index); })
|
||||||
| rpl::start_with_next(
|
| rpl::start_with_next(
|
||||||
|
@ -111,6 +114,9 @@ void WrapWidget::forceContentRepaint() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void WrapWidget::showTab(Tab tab) {
|
void WrapWidget::showTab(Tab tab) {
|
||||||
|
if (_tab == tab) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Expects(_content != nullptr);
|
Expects(_content != nullptr);
|
||||||
auto direction = (tab > _tab)
|
auto direction = (tab > _tab)
|
||||||
? SlideDirection::FromRight
|
? SlideDirection::FromRight
|
||||||
|
@ -132,6 +138,7 @@ void WrapWidget::showTab(Tab tab) {
|
||||||
showAnimated(direction, animationParams);
|
showAnimated(direction, animationParams);
|
||||||
|
|
||||||
_anotherTabMemento = std::move(newAnotherMemento);
|
_anotherTabMemento = std::move(newAnotherMemento);
|
||||||
|
_tab = tab;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WrapWidget::setupTabbedTop(const Section §ion) {
|
void WrapWidget::setupTabbedTop(const Section §ion) {
|
||||||
|
@ -241,7 +248,6 @@ void WrapWidget::finishShowContent() {
|
||||||
_topShadow->finishAnimating();
|
_topShadow->finishAnimating();
|
||||||
if (_topTabs) {
|
if (_topTabs) {
|
||||||
_topTabs->raise();
|
_topTabs->raise();
|
||||||
_topTabs->finishAnimating();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
#include "info/profile/info_profile_button.h"
|
#include "info/profile/info_profile_button.h"
|
||||||
#include "info/profile/info_profile_icon.h"
|
#include "info/profile/info_profile_icon.h"
|
||||||
#include "ui/widgets/discrete_sliders.h"
|
#include "ui/widgets/discrete_sliders.h"
|
||||||
|
#include "ui/widgets/shadow.h"
|
||||||
#include "ui/wrap/vertical_layout.h"
|
#include "ui/wrap/vertical_layout.h"
|
||||||
#include "styles/style_info.h"
|
#include "styles/style_info.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
@ -82,8 +83,12 @@ void InnerWidget::setupOtherTypes(rpl::producer<Wrap> &&wrap) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void InnerWidget::createOtherTypes() {
|
void InnerWidget::createOtherTypes() {
|
||||||
|
_otherTabsShadow.create(this);
|
||||||
|
_otherTabsShadow->show();
|
||||||
|
|
||||||
_otherTabs = nullptr;
|
_otherTabs = nullptr;
|
||||||
_otherTypes.create(this);
|
_otherTypes.create(this);
|
||||||
|
_otherTypes->show();
|
||||||
|
|
||||||
createTypeButtons();
|
createTypeButtons();
|
||||||
_otherTypes->add(object_ptr<BoxContentDivider>(_otherTypes));
|
_otherTypes->add(object_ptr<BoxContentDivider>(_otherTypes));
|
||||||
|
@ -157,6 +162,9 @@ void InnerWidget::createTabs() {
|
||||||
sections.push_back(lang(lng_media_type_videos).toUpper());
|
sections.push_back(lang(lng_media_type_videos).toUpper());
|
||||||
sections.push_back(lang(lng_media_type_files).toUpper());
|
sections.push_back(lang(lng_media_type_files).toUpper());
|
||||||
_otherTabs->setSections(sections);
|
_otherTabs->setSections(sections);
|
||||||
|
_otherTabs->setActiveSection(*TypeToTabIndex(type()));
|
||||||
|
_otherTabs->finishAnimating();
|
||||||
|
|
||||||
_otherTabs->sectionActivated()
|
_otherTabs->sectionActivated()
|
||||||
| rpl::map([](int index) { return TabIndexToType(index); })
|
| rpl::map([](int index) { return TabIndexToType(index); })
|
||||||
| rpl::start_with_next(
|
| rpl::start_with_next(
|
||||||
|
@ -204,7 +212,14 @@ void InnerWidget::switchToTab(Memento &&memento) {
|
||||||
auto type = memento.section().mediaType();
|
auto type = memento.section().mediaType();
|
||||||
_list = setupList(controller(), peer(), type);
|
_list = setupList(controller(), peer(), type);
|
||||||
restoreState(&memento);
|
restoreState(&memento);
|
||||||
_otherTabs->setActiveSection(*TypeToTabIndex(type));
|
_list->show();
|
||||||
|
_list->resizeToWidth(width());
|
||||||
|
refreshHeight();
|
||||||
|
if (_otherTypes) {
|
||||||
|
_otherTabsShadow->raise();
|
||||||
|
_otherTypes->raise();
|
||||||
|
_otherTabs->setActiveSection(*TypeToTabIndex(type));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
not_null<Window::Controller*> InnerWidget::controller() const {
|
not_null<Window::Controller*> InnerWidget::controller() const {
|
||||||
|
@ -239,6 +254,7 @@ int InnerWidget::resizeGetHeight(int newWidth) {
|
||||||
|
|
||||||
if (_otherTypes) {
|
if (_otherTypes) {
|
||||||
_otherTypes->resizeToWidth(newWidth);
|
_otherTypes->resizeToWidth(newWidth);
|
||||||
|
_otherTabsShadow->resizeToWidth(newWidth);
|
||||||
}
|
}
|
||||||
_list->resizeToWidth(newWidth);
|
_list->resizeToWidth(newWidth);
|
||||||
return recountHeight();
|
return recountHeight();
|
||||||
|
@ -255,7 +271,8 @@ int InnerWidget::recountHeight() {
|
||||||
auto top = 0;
|
auto top = 0;
|
||||||
if (_otherTypes) {
|
if (_otherTypes) {
|
||||||
_otherTypes->moveToLeft(0, top);
|
_otherTypes->moveToLeft(0, top);
|
||||||
top += _otherTypes->heightNoMargins();
|
top += _otherTypes->heightNoMargins() - st::lineWidth;
|
||||||
|
_otherTabsShadow->moveToLeft(0, top);
|
||||||
}
|
}
|
||||||
if (_list) {
|
if (_list) {
|
||||||
_list->moveToLeft(0, top);
|
_list->moveToLeft(0, top);
|
||||||
|
|
|
@ -78,6 +78,7 @@ private:
|
||||||
|
|
||||||
Ui::SettingsSlider *_otherTabs = nullptr;
|
Ui::SettingsSlider *_otherTabs = nullptr;
|
||||||
object_ptr<Ui::VerticalLayout> _otherTypes = { nullptr };
|
object_ptr<Ui::VerticalLayout> _otherTypes = { nullptr };
|
||||||
|
object_ptr<Ui::PlainShadow> _otherTabsShadow = { nullptr };
|
||||||
object_ptr<ListWidget> _list = { nullptr };
|
object_ptr<ListWidget> _list = { nullptr };
|
||||||
|
|
||||||
int _visibleTop = 0;
|
int _visibleTop = 0;
|
||||||
|
|
|
@ -20,8 +20,271 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
*/
|
*/
|
||||||
#include "info/media/info_media_list_widget.h"
|
#include "info/media/info_media_list_widget.h"
|
||||||
|
|
||||||
|
#include "overview/overview_layout.h"
|
||||||
|
#include "history/history_media_types.h"
|
||||||
|
#include "window/themes/window_theme.h"
|
||||||
|
#include "lang/lang_keys.h"
|
||||||
|
#include "styles/style_overview.h"
|
||||||
|
#include "styles/style_info.h"
|
||||||
|
|
||||||
|
namespace Layout = Overview::Layout;
|
||||||
|
|
||||||
namespace Info {
|
namespace Info {
|
||||||
namespace Media {
|
namespace Media {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr auto kIdsLimit = 256;
|
||||||
|
|
||||||
|
using ItemBase = Layout::ItemBase;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
ListWidget::CachedItem::CachedItem(std::unique_ptr<ItemBase> item)
|
||||||
|
: item(std::move(item)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ListWidget::CachedItem::~CachedItem() = default;
|
||||||
|
|
||||||
|
class ListWidget::Section {
|
||||||
|
public:
|
||||||
|
Section(Type type) : _type(type) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool addItem(not_null<ItemBase*> item);
|
||||||
|
bool empty() const {
|
||||||
|
return _items.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void resizeToWidth(int newWidth);
|
||||||
|
int height() const {
|
||||||
|
return _height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void paint(
|
||||||
|
Painter &p,
|
||||||
|
QRect clip,
|
||||||
|
int outerWidth,
|
||||||
|
TimeMs ms) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int headerHeight() const;
|
||||||
|
void appendItem(not_null<ItemBase*> item);
|
||||||
|
void setHeader(not_null<ItemBase*> item);
|
||||||
|
bool belongsHere(not_null<ItemBase*> item) const;
|
||||||
|
int countRowHeight(not_null<ItemBase*> item) const;
|
||||||
|
|
||||||
|
Type _type = Type::Photo;
|
||||||
|
Text _header;
|
||||||
|
std::vector<not_null<ItemBase*>> _items;
|
||||||
|
int _itemsLeft = 0;
|
||||||
|
int _itemsTop = 0;
|
||||||
|
int _itemWidth = 0;
|
||||||
|
int _itemsInRow = 1;
|
||||||
|
int _rowsCount = 0;
|
||||||
|
int _height = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ListWidget::Section::addItem(not_null<ItemBase*> item) {
|
||||||
|
if (_items.empty() || belongsHere(item)) {
|
||||||
|
if (_items.empty()) setHeader(item);
|
||||||
|
appendItem(item);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListWidget::Section::setHeader(not_null<ItemBase*> item) {
|
||||||
|
auto text = [&] {
|
||||||
|
auto date = item->getItem()->date.date();
|
||||||
|
switch (_type) {
|
||||||
|
case Type::Photo:
|
||||||
|
case Type::Video:
|
||||||
|
case Type::RoundFile:
|
||||||
|
case Type::VoiceFile:
|
||||||
|
case Type::File:
|
||||||
|
return langMonthFull(date);
|
||||||
|
|
||||||
|
case Type::Link:
|
||||||
|
return langDayOfMonthFull(date);
|
||||||
|
|
||||||
|
case Type::MusicFile:
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
Unexpected("Type in ListWidget::Section::setHeader()");
|
||||||
|
}();
|
||||||
|
_header.setText(st::infoMediaHeaderStyle, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ListWidget::Section::belongsHere(
|
||||||
|
not_null<ItemBase*> item) const {
|
||||||
|
Expects(!_items.empty());
|
||||||
|
auto date = item->getItem()->date.date();
|
||||||
|
auto myDate = _items.back()->getItem()->date.date();
|
||||||
|
|
||||||
|
switch (_type) {
|
||||||
|
case Type::Photo:
|
||||||
|
case Type::Video:
|
||||||
|
case Type::RoundFile:
|
||||||
|
case Type::VoiceFile:
|
||||||
|
case Type::File:
|
||||||
|
return date.year() == myDate.year()
|
||||||
|
&& date.month() == myDate.month();
|
||||||
|
|
||||||
|
case Type::Link:
|
||||||
|
return date.year() == myDate.year()
|
||||||
|
&& date.month() == myDate.month()
|
||||||
|
&& date.day() == myDate.day();
|
||||||
|
|
||||||
|
case Type::MusicFile:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Unexpected("Type in ListWidget::Section::belongsHere()");
|
||||||
|
}
|
||||||
|
|
||||||
|
int ListWidget::Section::countRowHeight(
|
||||||
|
not_null<ItemBase*> item) const {
|
||||||
|
switch (_type) {
|
||||||
|
case Type::Photo:
|
||||||
|
case Type::Video:
|
||||||
|
case Type::RoundFile:
|
||||||
|
return _itemWidth + st::infoMediaSkip;
|
||||||
|
|
||||||
|
case Type::VoiceFile:
|
||||||
|
case Type::File:
|
||||||
|
case Type::Link:
|
||||||
|
case Type::MusicFile:
|
||||||
|
return item->height();
|
||||||
|
}
|
||||||
|
Unexpected("Type in ListWidget::Section::countRowHeight()");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListWidget::Section::appendItem(not_null<ItemBase*> item) {
|
||||||
|
_items.push_back(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListWidget::Section::paint(
|
||||||
|
Painter &p,
|
||||||
|
QRect clip,
|
||||||
|
int outerWidth,
|
||||||
|
TimeMs ms) const {
|
||||||
|
auto baseIndex = 0;
|
||||||
|
auto top = _itemsTop;
|
||||||
|
auto header = headerHeight();
|
||||||
|
if (header) {
|
||||||
|
p.setPen(st::infoMediaHeaderFg);
|
||||||
|
_header.drawLeftElided(
|
||||||
|
p,
|
||||||
|
st::infoMediaHeaderPosition.x(),
|
||||||
|
st::infoMediaHeaderPosition.y(),
|
||||||
|
outerWidth - 2 * st::infoMediaHeaderPosition.x(),
|
||||||
|
outerWidth);
|
||||||
|
top += header;
|
||||||
|
}
|
||||||
|
auto fromitem = floorclamp(
|
||||||
|
clip.x() - _itemsLeft,
|
||||||
|
_itemWidth,
|
||||||
|
0,
|
||||||
|
_itemsInRow);
|
||||||
|
auto tillitem = ceilclamp(
|
||||||
|
clip.x() + clip.width() - _itemsLeft,
|
||||||
|
_itemWidth,
|
||||||
|
0,
|
||||||
|
_itemsInRow);
|
||||||
|
Layout::PaintContext context(ms, false);
|
||||||
|
context.isAfterDate = (header > 0);
|
||||||
|
|
||||||
|
// #TODO ranges, binary search for visible slice.
|
||||||
|
for (auto row = 0; row != _rowsCount; ++row) {
|
||||||
|
auto rowHeight = countRowHeight(_items[baseIndex]);
|
||||||
|
auto increment = gsl::finally([&] {
|
||||||
|
top += rowHeight;
|
||||||
|
baseIndex += _itemsInRow;
|
||||||
|
context.isAfterDate = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (top >= clip.y() + clip.height()) {
|
||||||
|
break;
|
||||||
|
} else if (top + rowHeight <= clip.y()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (auto col = fromitem; col != tillitem; ++col) {
|
||||||
|
auto index = baseIndex + col;
|
||||||
|
if (index >= int(_items.size())) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto item = _items[index];
|
||||||
|
auto left = _itemsLeft
|
||||||
|
+ col * (_itemWidth + st::infoMediaSkip);
|
||||||
|
p.translate(left, top);
|
||||||
|
item->paint(
|
||||||
|
p,
|
||||||
|
clip.translated(-left, -top),
|
||||||
|
TextSelection(),
|
||||||
|
&context);
|
||||||
|
p.translate(-left, -top);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ListWidget::Section::headerHeight() const {
|
||||||
|
return _header.isEmpty() ? 0 : st::infoMediaHeaderHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListWidget::Section::resizeToWidth(int newWidth) {
|
||||||
|
auto minWidth = st::infoMediaMinGridSize + st::infoMediaSkip * 2;
|
||||||
|
if (newWidth < minWidth) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_height = headerHeight();
|
||||||
|
switch (_type) {
|
||||||
|
case Type::Photo:
|
||||||
|
case Type::Video:
|
||||||
|
case Type::RoundFile: {
|
||||||
|
_itemsLeft = st::infoMediaSkip;
|
||||||
|
_itemsTop = st::infoMediaSkip;
|
||||||
|
_itemsInRow = (newWidth - _itemsLeft)
|
||||||
|
/ (st::infoMediaMinGridSize + st::infoMediaSkip);
|
||||||
|
_itemWidth = ((newWidth - _itemsLeft) / _itemsInRow)
|
||||||
|
- st::infoMediaSkip;
|
||||||
|
auto itemHeight = _itemWidth + st::infoMediaSkip;
|
||||||
|
_rowsCount = (int(_items.size()) + _itemsInRow - 1)
|
||||||
|
/ _itemsInRow;
|
||||||
|
_height += _itemsTop + _rowsCount * itemHeight;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case Type::VoiceFile:
|
||||||
|
case Type::File:
|
||||||
|
case Type::MusicFile: {
|
||||||
|
_itemsLeft = 0;
|
||||||
|
_itemsTop = 0;
|
||||||
|
_itemsInRow = 1;
|
||||||
|
_itemWidth = newWidth;
|
||||||
|
auto itemHeight = _items.empty() ? 0 : _items.front()->height();
|
||||||
|
_rowsCount = _items.size();
|
||||||
|
_height += _rowsCount * itemHeight;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case Type::Link:
|
||||||
|
_itemsLeft = 0;
|
||||||
|
_itemsTop = 0;
|
||||||
|
_itemsInRow = 1;
|
||||||
|
_itemWidth = newWidth;
|
||||||
|
auto top = 0;
|
||||||
|
for (auto item : _items) {
|
||||||
|
top += item->resizeGetHeight(_itemWidth);
|
||||||
|
}
|
||||||
|
_height += top;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_type != Type::Link) {
|
||||||
|
for (auto item : _items) {
|
||||||
|
item->resizeGetHeight(_itemWidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ListWidget::ListWidget(
|
ListWidget::ListWidget(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
|
@ -31,8 +294,194 @@ ListWidget::ListWidget(
|
||||||
: RpWidget(parent)
|
: RpWidget(parent)
|
||||||
, _controller(controller)
|
, _controller(controller)
|
||||||
, _peer(peer)
|
, _peer(peer)
|
||||||
, _type(type) {
|
, _type(type)
|
||||||
|
, _slice(sliceKey()) {
|
||||||
|
refreshViewer();
|
||||||
|
ObservableViewer(*Window::Theme::Background())
|
||||||
|
| rpl::start_with_next([this](const auto &update) {
|
||||||
|
if (update.paletteChanged()) {
|
||||||
|
invalidatePaletteCache();
|
||||||
|
}
|
||||||
|
}, lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ListWidget::invalidatePaletteCache() {
|
||||||
|
for (auto &layout : _layouts) {
|
||||||
|
layout.second.item->invalidateCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedMediaMergedSlice::Key ListWidget::sliceKey() const {
|
||||||
|
auto universalId = _universalAroundId;
|
||||||
|
using Key = SharedMediaMergedSlice::Key;
|
||||||
|
if (auto migrateTo = _peer->migrateTo()) {
|
||||||
|
return Key(migrateTo->id, _peer->id, _type, universalId);
|
||||||
|
} else if (auto migrateFrom = _peer->migrateFrom()) {
|
||||||
|
return Key(_peer->id, migrateFrom->id, _type, universalId);
|
||||||
|
}
|
||||||
|
return Key(_peer->id, 0, _type, universalId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListWidget::refreshViewer() {
|
||||||
|
SharedMediaMergedViewer(
|
||||||
|
sliceKey(),
|
||||||
|
countIdsLimit(),
|
||||||
|
countIdsLimit())
|
||||||
|
| rpl::start_with_next([this](SharedMediaMergedSlice &&slice) {
|
||||||
|
_slice = std::move(slice);
|
||||||
|
refreshRows();
|
||||||
|
}, _viewerLifetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ListWidget::countIdsLimit() const {
|
||||||
|
return kIdsLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemBase *ListWidget::getLayout(const FullMsgId &itemId) {
|
||||||
|
auto it = _layouts.find(itemId);
|
||||||
|
if (it == _layouts.end()) {
|
||||||
|
if (auto layout = createLayout(itemId, _type)) {
|
||||||
|
layout->initDimensions();
|
||||||
|
it = _layouts.emplace(itemId, std::move(layout)).first;
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it->second.stale = false;
|
||||||
|
return it->second.item.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ItemBase> ListWidget::createLayout(
|
||||||
|
const FullMsgId &itemId,
|
||||||
|
Type type) {
|
||||||
|
auto item = App::histItemById(itemId);
|
||||||
|
if (!item) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto getPhoto = [&]() -> PhotoData* {
|
||||||
|
if (auto media = item->getMedia()) {
|
||||||
|
if (media->type() == MediaTypePhoto) {
|
||||||
|
return static_cast<HistoryPhoto*>(media)->photo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
auto getFile = [&]() -> DocumentData* {
|
||||||
|
if (auto media = item->getMedia()) {
|
||||||
|
return media->getDocument();
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
switch (type) {
|
||||||
|
case Type::Photo:
|
||||||
|
if (auto photo = getPhoto()) {
|
||||||
|
return std::make_unique<Layout::Photo>(photo, item);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
case Type::Video:
|
||||||
|
if (auto file = getFile()) {
|
||||||
|
return std::make_unique<Layout::Video>(file, item);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
case Type::File:
|
||||||
|
if (auto file = getFile()) {
|
||||||
|
return std::make_unique<Layout::Document>(file, item, st::overviewFileLayout);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
case Type::MusicFile:
|
||||||
|
if (auto file = getFile()) {
|
||||||
|
return std::make_unique<Layout::Document>(file, item, st::overviewFileLayout);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
case Type::VoiceFile:
|
||||||
|
if (auto file = getFile()) {
|
||||||
|
return std::make_unique<Layout::Voice>(file, item, st::overviewFileLayout);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
case Type::Link:
|
||||||
|
return std::make_unique<Layout::Link>(item->getMedia(), item);
|
||||||
|
case Type::RoundFile:
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
Unexpected("Type in ListWidget::createLayout()");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListWidget::refreshRows() {
|
||||||
|
markLayoutsStale();
|
||||||
|
|
||||||
|
_sections.clear();
|
||||||
|
auto section = Section(_type);
|
||||||
|
auto count = _slice.size();
|
||||||
|
for (auto i = count; i != 0;) {
|
||||||
|
auto itemId = _slice[--i];
|
||||||
|
if (auto layout = getLayout(itemId)) {
|
||||||
|
if (!section.addItem(layout)) {
|
||||||
|
_sections.push_back(std::move(section));
|
||||||
|
section = Section(_type);
|
||||||
|
section.addItem(layout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!section.empty()) {
|
||||||
|
_sections.push_back(std::move(section));
|
||||||
|
}
|
||||||
|
|
||||||
|
clearStaleLayouts();
|
||||||
|
|
||||||
|
resizeToWidth(width());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListWidget::markLayoutsStale() {
|
||||||
|
for (auto &layout : _layouts) {
|
||||||
|
layout.second.stale = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ListWidget::resizeGetHeight(int newWidth) {
|
||||||
|
for (auto §ion : _sections) {
|
||||||
|
section.resizeToWidth(newWidth);
|
||||||
|
}
|
||||||
|
return recountHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListWidget::paintEvent(QPaintEvent *e) {
|
||||||
|
Painter p(this);
|
||||||
|
|
||||||
|
auto outerWidth = width();
|
||||||
|
auto clip = e->rect();
|
||||||
|
auto ms = getms();
|
||||||
|
auto top = st::infoMediaMargin.top();
|
||||||
|
p.translate(0, top);
|
||||||
|
clip = clip.translated(0, -top);
|
||||||
|
for (auto §ion : _sections) {
|
||||||
|
section.paint(p, clip, outerWidth, ms);
|
||||||
|
auto height = section.height();
|
||||||
|
p.translate(0, height);
|
||||||
|
clip = clip.translated(0, -height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ListWidget::recountHeight() {
|
||||||
|
auto result = 0;
|
||||||
|
for (auto §ion : _sections) {
|
||||||
|
result += section.height();
|
||||||
|
}
|
||||||
|
return st::infoMediaMargin.top()
|
||||||
|
+ result
|
||||||
|
+ st::infoMediaMargin.bottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListWidget::clearStaleLayouts() {
|
||||||
|
for (auto i = _layouts.begin(); i != _layouts.end();) {
|
||||||
|
if (i->second.stale) {
|
||||||
|
i = _layouts.erase(i);
|
||||||
|
} else {
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListWidget::~ListWidget() = default;
|
||||||
|
|
||||||
} // namespace Media
|
} // namespace Media
|
||||||
} // namespace Info
|
} // namespace Info
|
||||||
|
|
|
@ -22,6 +22,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
|
|
||||||
#include "ui/rp_widget.h"
|
#include "ui/rp_widget.h"
|
||||||
#include "info/media/info_media_widget.h"
|
#include "info/media/info_media_widget.h"
|
||||||
|
#include "history/history_shared_media.h"
|
||||||
|
|
||||||
|
namespace Overview {
|
||||||
|
namespace Layout {
|
||||||
|
class ItemBase;
|
||||||
|
} // namespace Layout
|
||||||
|
} // namespace Overview
|
||||||
|
|
||||||
namespace Window {
|
namespace Window {
|
||||||
class Controller;
|
class Controller;
|
||||||
|
@ -49,11 +56,51 @@ public:
|
||||||
return _type;
|
return _type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~ListWidget();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int resizeGetHeight(int newWidth) override;
|
||||||
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
using ItemBase = Overview::Layout::ItemBase;
|
||||||
|
|
||||||
|
int recountHeight();
|
||||||
|
|
||||||
|
void refreshViewer();
|
||||||
|
void invalidatePaletteCache();
|
||||||
|
void refreshRows();
|
||||||
|
int countIdsLimit() const;
|
||||||
|
SharedMediaMergedSlice::Key sliceKey() const;
|
||||||
|
ItemBase *getLayout(const FullMsgId &itemId);
|
||||||
|
std::unique_ptr<ItemBase> createLayout(
|
||||||
|
const FullMsgId &itemId,
|
||||||
|
Type type);
|
||||||
|
|
||||||
|
void markLayoutsStale();
|
||||||
|
void clearStaleLayouts();
|
||||||
|
|
||||||
not_null<Window::Controller*> _controller;
|
not_null<Window::Controller*> _controller;
|
||||||
not_null<PeerData*> _peer;
|
not_null<PeerData*> _peer;
|
||||||
Type _type = Type::Photo;
|
Type _type = Type::Photo;
|
||||||
|
|
||||||
|
MsgId _universalAroundId = ServerMaxMsgId - 1;
|
||||||
|
SharedMediaMergedSlice _slice;
|
||||||
|
|
||||||
|
struct CachedItem {
|
||||||
|
CachedItem(std::unique_ptr<ItemBase> item);
|
||||||
|
~CachedItem();
|
||||||
|
|
||||||
|
std::unique_ptr<ItemBase> item;
|
||||||
|
bool stale = false;
|
||||||
|
};
|
||||||
|
std::map<FullMsgId, CachedItem> _layouts;
|
||||||
|
|
||||||
|
class Section;
|
||||||
|
std::vector<Section> _sections;
|
||||||
|
|
||||||
|
rpl::lifetime _viewerLifetime;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Media
|
} // namespace Media
|
||||||
|
|
|
@ -191,13 +191,15 @@ rpl::producer<int> MembersCountValue(
|
||||||
rpl::producer<int> SharedMediaCountValue(
|
rpl::producer<int> SharedMediaCountValue(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
Storage::SharedMediaType type) {
|
Storage::SharedMediaType type) {
|
||||||
auto initial = peer->migrateFrom() ? peer->migrateFrom() : peer;
|
auto real = peer->migrateTo() ? peer->migrateTo() : peer;
|
||||||
auto migrated = initial->migrateTo();
|
auto migrated = real->migrateFrom()
|
||||||
|
? real->migrateFrom()
|
||||||
|
: nullptr;
|
||||||
auto aroundId = 0;
|
auto aroundId = 0;
|
||||||
auto limit = 0;
|
auto limit = 0;
|
||||||
auto updated = SharedMediaMergedViewer(
|
auto updated = SharedMediaMergedViewer(
|
||||||
SharedMediaMergedSlice::Key(
|
SharedMediaMergedSlice::Key(
|
||||||
peer->id,
|
real->id,
|
||||||
migrated ? migrated->id : 0,
|
migrated ? migrated->id : 0,
|
||||||
type,
|
type,
|
||||||
aroundId),
|
aroundId),
|
||||||
|
|
|
@ -69,13 +69,13 @@ public:
|
||||||
ItemBase(HistoryItem *parent) : _parent(parent) {
|
ItemBase(HistoryItem *parent) : _parent(parent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemBase *toMediaItem() override {
|
ItemBase *toMediaItem() final override {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
const ItemBase *toMediaItem() const override {
|
const ItemBase *toMediaItem() const final override {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
HistoryItem *getItem() const override {
|
HistoryItem *getItem() const final override {
|
||||||
return _parent;
|
return _parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue