Support single download file thumbnail display.

This commit is contained in:
John Preston 2022-03-07 18:10:01 +04:00
parent c04a789d70
commit bde79210ca
4 changed files with 202 additions and 67 deletions

View file

@ -10,9 +10,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h"
#include "data/data_photo.h"
#include "data/data_document.h"
#include "data/data_document_media.h"
#include "data/data_changes.h"
#include "data/data_user.h"
#include "data/data_channel.h"
#include "data/data_file_origin.h"
#include "base/unixtime.h"
#include "base/random.h"
#include "main/main_session.h"
@ -846,26 +848,99 @@ rpl::producer<Ui::DownloadBarProgress> MakeDownloadBarProgress() {
}
rpl::producer<Ui::DownloadBarContent> MakeDownloadBarContent() {
return [](auto consumer) {
auto lifetime = rpl::lifetime();
struct State {
DocumentData *document = nullptr;
std::shared_ptr<Data::DocumentMedia> media;
rpl::lifetime downloadTaskLifetime;
QImage thumbnail;
Fn<void()> push;
};
const auto state = lifetime.make_state<State>();
auto &manager = Core::App().downloadManager();
return rpl::single(
rpl::empty_value()
) | rpl::then(
manager.loadingListChanges() | rpl::to_empty
) | rpl::map([=, &manager] {
auto result = Ui::DownloadBarContent();
for (const auto id : manager.loadingList()) {
if (result.singleName.text.isEmpty()) {
const auto document = id->object.document;
result.singleName = Ui::Text::FormatDownloadsName(document);
result.singleThumbnail = QImage();
const auto resolveThumbnailRecursive = [=](auto &&self) -> bool {
if (state->document
&& (!state->document->hasThumbnail()
|| state->document->thumbnailFailed())) {
state->media = nullptr;
}
++result.count;
if (id->done) {
++result.done;
if (!state->media) {
state->downloadTaskLifetime.destroy();
if (!state->thumbnail.isNull()) {
return false;
}
state->thumbnail = QImage();
return true;
}
return result;
if (const auto image = state->media->thumbnail()) {
state->thumbnail = image->original();
state->downloadTaskLifetime.destroy();
state->media = nullptr;
return true;
} else if (const auto embed = state->media->thumbnailInline()) {
if (!state->thumbnail.isNull()) {
return false;
}
state->thumbnail = Images::Prepare(embed->original(), 0, {
.options = Images::Option::Blur,
});
}
state->document->session().downloaderTaskFinished(
) | rpl::filter([=] {
return self(self);
}) | rpl::start_with_next(
state->push,
state->downloadTaskLifetime);
return !state->thumbnail.isNull();
};
const auto resolveThumbnail = [=] {
return resolveThumbnailRecursive(resolveThumbnailRecursive);
};
state->push = [=, &manager] {
auto content = Ui::DownloadBarContent();
auto single = (const Data::DownloadObject*) nullptr;
for (const auto id : manager.loadingList()) {
if (!single) {
single = &id->object;
}
++content.count;
if (id->done) {
++content.done;
}
}
if (content.count == 1) {
const auto document = single->document;
const auto thumbnailed = (single->item
&& document->hasThumbnail())
? document
: nullptr;
if (state->document != thumbnailed) {
state->document = thumbnailed;
state->media = thumbnailed
? thumbnailed->createMediaView()
: nullptr;
state->media->thumbnailWanted(single->item->fullId());
state->thumbnail = QImage();
resolveThumbnail();
}
content.singleName = Ui::Text::FormatDownloadsName(
document);
content.singleThumbnail = state->thumbnail;
}
consumer.put_next(std::move(content));
};
manager.loadingListChanges(
) | rpl::start_with_next(state->push, lifetime);
state->push();
return lifetime;
};
}
} // namespace Data

View file

@ -1910,6 +1910,9 @@ void Widget::onDialogMoved(int movedFrom, int movedTo) {
Widget::~Widget() {
cancelSearchRequest();
// Destructor may hide the bar and attempt to double-destroy it.
base::take(_downloadBar);
}
} // namespace Dialogs

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/buttons.h"
#include "ui/text/format_values.h"
#include "ui/text/text_utilities.h"
#include "ui/image/image_prepare.h"
#include "lang/lang_keys.h"
#include "styles/style_dialogs.h"
@ -18,6 +19,17 @@ namespace {
constexpr auto kFullArcLength = 360 * 16;
[[nodiscard]] QImage Make(const QImage &image, int size) {
if (image.isNull()) {
return QImage();
}
auto result = image.scaledToWidth(
size * style::DevicePixelRatio(),
Qt::SmoothTransformation);
result.setDevicePixelRatio(style::DevicePixelRatio());
return result;
}
} // namespace
DownloadBar::DownloadBar(
@ -71,6 +83,7 @@ void DownloadBar::show(DownloadBarContent &&content) {
_finished ? 1. : 0.,
st::widgetFadeDuration);
}
refreshThumbnail();
_title.setMarkedText(
st::defaultTextStyle,
(content.count > 1
@ -80,19 +93,47 @@ void DownloadBar::show(DownloadBarContent &&content) {
refreshInfo(_progress.current());
}
void DownloadBar::refreshThumbnail() {
if (_content.singleThumbnail.isNull()) {
_thumbnail = _thumbnailDone = QImage();
_thumbnailCacheKey = 0;
return;
}
const auto cacheKey = _content.singleThumbnail.cacheKey();
if (_thumbnailCacheKey == cacheKey) {
return;
}
_thumbnailCacheKey = cacheKey;
_thumbnailLarge = _content.singleThumbnail;
_thumbnailLarge.detach();
const auto width = _thumbnailLarge.width();
const auto height = _thumbnailLarge.height();
if (width != height) {
const auto size = std::min(width, height);
_thumbnailLarge = _thumbnailLarge.copy(
(width - size) / 2,
(height - size) / 2,
size,
size);
}
const auto size = st::downloadLoadingSize;
const auto added = 3 * st::downloadLoadingLine;
const auto loadingsize = size;
const auto donesize = size + (added - st::downloadLoadingLine) * 2;
const auto make = [&](int size) {
return Images::Circle(Make(_thumbnailLarge, size));
};
_thumbnail = make(loadingsize);
_thumbnailDone = make(donesize);
_thumbnailLarge = Images::Circle(std::move(_thumbnailLarge));
}
void DownloadBar::refreshIcon() {
_documentIconOriginal = st::downloadIconDocument.instance(
_documentIconLarge = st::downloadIconDocument.instance(
st::windowFgActive->c,
style::kScaleMax / style::DevicePixelRatio());
const auto make = [&](int size) {
auto result = _documentIconOriginal.scaledToWidth(
size * style::DevicePixelRatio(),
Qt::SmoothTransformation);
result.setDevicePixelRatio(style::DevicePixelRatio());
return result;
};
_documentIcon = make(st::downloadIconSize);
_documentIconDone = make(st::downloadIconSizeDone);
_documentIcon = Make(_documentIconLarge, st::downloadIconSize);
_documentIconDone = Make(_documentIconLarge, st::downloadIconSizeDone);
}
void DownloadBar::refreshInfo(const DownloadBarProgress &progress) {
@ -156,17 +197,10 @@ void DownloadBar::paint(Painter &p, QRect clip) {
size + added * 2,
size + added * 2);
if (full.intersects(clip)) {
const auto done = (finished == 1.);
const auto loading = _radial.computeState();
{
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush(st::windowBgActive);
const auto shift = st::downloadLoadingLine
+ (1. - finished) * (added - st::downloadLoadingLine);
p.drawEllipse(QRectF(full).marginsRemoved(
{ shift, shift, shift, shift }));
if (loading.shown > 0) {
auto hq = PainterHighQualityEnabler(p);
p.setOpacity(loading.shown);
auto pen = st::windowBgActive->p;
pen.setWidth(st::downloadLoadingLine);
@ -181,16 +215,25 @@ void DownloadBar::paint(Painter &p, QRect clip) {
}
p.setOpacity(1.);
}
}
const auto shift = st::downloadLoadingLine
+ (1. - finished) * (added - st::downloadLoadingLine);
const auto ellipse = QRectF(full).marginsRemoved(
{ shift, shift, shift, shift });
if (_thumbnail.isNull()) {
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush(st::windowBgActive);
p.drawEllipse(ellipse);
const auto sizeLoading = st::downloadIconSize;
if (finished == 0. || finished == 1.) {
const auto size = (finished == 1.)
if (finished == 0. || done) {
const auto size = done
? st::downloadIconSizeDone
: sizeLoading;
const auto image = done ? _documentIconDone : _documentIcon;
p.drawImage(
full.x() + (full.width() - size) / 2,
full.y() + (full.height() - size) / 2,
(finished == 1.) ? _documentIconDone : _documentIcon);
image);
} else {
auto hq = PainterHighQualityEnabler(p);
const auto size = sizeLoading
@ -201,7 +244,16 @@ void DownloadBar::paint(Painter &p, QRect clip) {
full.y() + (full.height() - size) / 2.,
size,
size),
_documentIconOriginal);
_documentIconLarge);
}
} else if (finished == 0. || done) {
p.drawImage(
base::SafeRound(ellipse.x()),
base::SafeRound(ellipse.y()),
done ? _thumbnailDone : _thumbnail);
} else {
auto hq = PainterHighQualityEnabler(p);
p.drawImage(ellipse, _thumbnailLarge);
}
}

View file

@ -50,6 +50,7 @@ public:
private:
void paint(Painter &p, QRect clip);
void refreshIcon();
void refreshThumbnail();
void refreshInfo(const DownloadBarProgress &progress);
void radialAnimationCallback(crl::time now);
[[nodiscard]] float64 computeProgress() const;
@ -60,9 +61,13 @@ private:
rpl::variable<DownloadBarProgress> _progress;
Ui::Animations::Simple _finishedAnimation;
bool _finished = false;
QImage _documentIconOriginal;
QImage _documentIconLarge;
QImage _documentIcon;
QImage _documentIconDone;
qint64 _thumbnailCacheKey = 0;
QImage _thumbnailLarge;
QImage _thumbnail;
QImage _thumbnailDone;
Text::String _title;
Text::String _info;
RadialAnimation _radial;