mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-07 23:53:58 +02:00
Support single download file thumbnail display.
This commit is contained in:
parent
c04a789d70
commit
bde79210ca
4 changed files with 202 additions and 67 deletions
|
@ -10,9 +10,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_photo.h"
|
#include "data/data_photo.h"
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
|
#include "data/data_document_media.h"
|
||||||
#include "data/data_changes.h"
|
#include "data/data_changes.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
#include "data/data_channel.h"
|
#include "data/data_channel.h"
|
||||||
|
#include "data/data_file_origin.h"
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
#include "base/random.h"
|
#include "base/random.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
@ -846,26 +848,99 @@ rpl::producer<Ui::DownloadBarProgress> MakeDownloadBarProgress() {
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<Ui::DownloadBarContent> MakeDownloadBarContent() {
|
rpl::producer<Ui::DownloadBarContent> MakeDownloadBarContent() {
|
||||||
auto &manager = Core::App().downloadManager();
|
return [](auto consumer) {
|
||||||
return rpl::single(
|
auto lifetime = rpl::lifetime();
|
||||||
rpl::empty_value()
|
|
||||||
) | rpl::then(
|
struct State {
|
||||||
manager.loadingListChanges() | rpl::to_empty
|
DocumentData *document = nullptr;
|
||||||
) | rpl::map([=, &manager] {
|
std::shared_ptr<Data::DocumentMedia> media;
|
||||||
auto result = Ui::DownloadBarContent();
|
rpl::lifetime downloadTaskLifetime;
|
||||||
for (const auto id : manager.loadingList()) {
|
QImage thumbnail;
|
||||||
if (result.singleName.text.isEmpty()) {
|
Fn<void()> push;
|
||||||
const auto document = id->object.document;
|
};
|
||||||
result.singleName = Ui::Text::FormatDownloadsName(document);
|
|
||||||
result.singleThumbnail = QImage();
|
const auto state = lifetime.make_state<State>();
|
||||||
|
auto &manager = Core::App().downloadManager();
|
||||||
|
|
||||||
|
const auto resolveThumbnailRecursive = [=](auto &&self) -> bool {
|
||||||
|
if (state->document
|
||||||
|
&& (!state->document->hasThumbnail()
|
||||||
|
|| state->document->thumbnailFailed())) {
|
||||||
|
state->media = nullptr;
|
||||||
}
|
}
|
||||||
++result.count;
|
if (!state->media) {
|
||||||
if (id->done) {
|
state->downloadTaskLifetime.destroy();
|
||||||
++result.done;
|
if (!state->thumbnail.isNull()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
state->thumbnail = QImage();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
if (const auto image = state->media->thumbnail()) {
|
||||||
return result;
|
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
|
} // namespace Data
|
||||||
|
|
|
@ -1910,6 +1910,9 @@ void Widget::onDialogMoved(int movedFrom, int movedTo) {
|
||||||
|
|
||||||
Widget::~Widget() {
|
Widget::~Widget() {
|
||||||
cancelSearchRequest();
|
cancelSearchRequest();
|
||||||
|
|
||||||
|
// Destructor may hide the bar and attempt to double-destroy it.
|
||||||
|
base::take(_downloadBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Dialogs
|
} // namespace Dialogs
|
||||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/text/format_values.h"
|
#include "ui/text/format_values.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
|
#include "ui/image/image_prepare.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "styles/style_dialogs.h"
|
#include "styles/style_dialogs.h"
|
||||||
|
|
||||||
|
@ -18,6 +19,17 @@ namespace {
|
||||||
|
|
||||||
constexpr auto kFullArcLength = 360 * 16;
|
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
|
} // namespace
|
||||||
|
|
||||||
DownloadBar::DownloadBar(
|
DownloadBar::DownloadBar(
|
||||||
|
@ -71,6 +83,7 @@ void DownloadBar::show(DownloadBarContent &&content) {
|
||||||
_finished ? 1. : 0.,
|
_finished ? 1. : 0.,
|
||||||
st::widgetFadeDuration);
|
st::widgetFadeDuration);
|
||||||
}
|
}
|
||||||
|
refreshThumbnail();
|
||||||
_title.setMarkedText(
|
_title.setMarkedText(
|
||||||
st::defaultTextStyle,
|
st::defaultTextStyle,
|
||||||
(content.count > 1
|
(content.count > 1
|
||||||
|
@ -80,19 +93,47 @@ void DownloadBar::show(DownloadBarContent &&content) {
|
||||||
refreshInfo(_progress.current());
|
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() {
|
void DownloadBar::refreshIcon() {
|
||||||
_documentIconOriginal = st::downloadIconDocument.instance(
|
_documentIconLarge = st::downloadIconDocument.instance(
|
||||||
st::windowFgActive->c,
|
st::windowFgActive->c,
|
||||||
style::kScaleMax / style::DevicePixelRatio());
|
style::kScaleMax / style::DevicePixelRatio());
|
||||||
const auto make = [&](int size) {
|
_documentIcon = Make(_documentIconLarge, st::downloadIconSize);
|
||||||
auto result = _documentIconOriginal.scaledToWidth(
|
_documentIconDone = Make(_documentIconLarge, st::downloadIconSizeDone);
|
||||||
size * style::DevicePixelRatio(),
|
|
||||||
Qt::SmoothTransformation);
|
|
||||||
result.setDevicePixelRatio(style::DevicePixelRatio());
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
_documentIcon = make(st::downloadIconSize);
|
|
||||||
_documentIconDone = make(st::downloadIconSizeDone);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadBar::refreshInfo(const DownloadBarProgress &progress) {
|
void DownloadBar::refreshInfo(const DownloadBarProgress &progress) {
|
||||||
|
@ -156,52 +197,63 @@ void DownloadBar::paint(Painter &p, QRect clip) {
|
||||||
size + added * 2,
|
size + added * 2,
|
||||||
size + added * 2);
|
size + added * 2);
|
||||||
if (full.intersects(clip)) {
|
if (full.intersects(clip)) {
|
||||||
|
const auto done = (finished == 1.);
|
||||||
const auto loading = _radial.computeState();
|
const auto loading = _radial.computeState();
|
||||||
{
|
if (loading.shown > 0) {
|
||||||
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
|
p.setOpacity(loading.shown);
|
||||||
|
auto pen = st::windowBgActive->p;
|
||||||
|
pen.setWidth(st::downloadLoadingLine);
|
||||||
|
p.setPen(pen);
|
||||||
|
p.setBrush(Qt::NoBrush);
|
||||||
|
const auto m = added / 2.;
|
||||||
|
auto rect = QRectF(full).marginsRemoved({ m, m, m, m });
|
||||||
|
if (loading.arcLength < kFullArcLength) {
|
||||||
|
p.drawArc(rect, loading.arcFrom, loading.arcLength);
|
||||||
|
} else {
|
||||||
|
p.drawEllipse(rect);
|
||||||
|
}
|
||||||
|
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);
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
p.setPen(Qt::NoPen);
|
p.setPen(Qt::NoPen);
|
||||||
p.setBrush(st::windowBgActive);
|
p.setBrush(st::windowBgActive);
|
||||||
const auto shift = st::downloadLoadingLine
|
p.drawEllipse(ellipse);
|
||||||
+ (1. - finished) * (added - st::downloadLoadingLine);
|
const auto sizeLoading = st::downloadIconSize;
|
||||||
p.drawEllipse(QRectF(full).marginsRemoved(
|
if (finished == 0. || done) {
|
||||||
{ shift, shift, shift, shift }));
|
const auto size = done
|
||||||
|
? st::downloadIconSizeDone
|
||||||
if (loading.shown > 0) {
|
: sizeLoading;
|
||||||
p.setOpacity(loading.shown);
|
const auto image = done ? _documentIconDone : _documentIcon;
|
||||||
auto pen = st::windowBgActive->p;
|
p.drawImage(
|
||||||
pen.setWidth(st::downloadLoadingLine);
|
full.x() + (full.width() - size) / 2,
|
||||||
p.setPen(pen);
|
full.y() + (full.height() - size) / 2,
|
||||||
p.setBrush(Qt::NoBrush);
|
image);
|
||||||
const auto m = added / 2.;
|
} else {
|
||||||
auto rect = QRectF(full).marginsRemoved({ m, m, m, m });
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
if (loading.arcLength < kFullArcLength) {
|
const auto size = sizeLoading
|
||||||
p.drawArc(rect, loading.arcFrom, loading.arcLength);
|
+ (st::downloadIconSizeDone - sizeLoading) * finished;
|
||||||
} else {
|
p.drawImage(
|
||||||
p.drawEllipse(rect);
|
QRectF(
|
||||||
}
|
full.x() + (full.width() - size) / 2.,
|
||||||
p.setOpacity(1.);
|
full.y() + (full.height() - size) / 2.,
|
||||||
|
size,
|
||||||
|
size),
|
||||||
|
_documentIconLarge);
|
||||||
}
|
}
|
||||||
}
|
} else if (finished == 0. || done) {
|
||||||
const auto sizeLoading = st::downloadIconSize;
|
|
||||||
if (finished == 0. || finished == 1.) {
|
|
||||||
const auto size = (finished == 1.)
|
|
||||||
? st::downloadIconSizeDone
|
|
||||||
: sizeLoading;
|
|
||||||
p.drawImage(
|
p.drawImage(
|
||||||
full.x() + (full.width() - size) / 2,
|
base::SafeRound(ellipse.x()),
|
||||||
full.y() + (full.height() - size) / 2,
|
base::SafeRound(ellipse.y()),
|
||||||
(finished == 1.) ? _documentIconDone : _documentIcon);
|
done ? _thumbnailDone : _thumbnail);
|
||||||
} else {
|
} else {
|
||||||
auto hq = PainterHighQualityEnabler(p);
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
const auto size = sizeLoading
|
p.drawImage(ellipse, _thumbnailLarge);
|
||||||
+ (st::downloadIconSizeDone - sizeLoading) * finished;
|
|
||||||
p.drawImage(
|
|
||||||
QRectF(
|
|
||||||
full.x() + (full.width() - size) / 2.,
|
|
||||||
full.y() + (full.height() - size) / 2.,
|
|
||||||
size,
|
|
||||||
size),
|
|
||||||
_documentIconOriginal);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,7 @@ public:
|
||||||
private:
|
private:
|
||||||
void paint(Painter &p, QRect clip);
|
void paint(Painter &p, QRect clip);
|
||||||
void refreshIcon();
|
void refreshIcon();
|
||||||
|
void refreshThumbnail();
|
||||||
void refreshInfo(const DownloadBarProgress &progress);
|
void refreshInfo(const DownloadBarProgress &progress);
|
||||||
void radialAnimationCallback(crl::time now);
|
void radialAnimationCallback(crl::time now);
|
||||||
[[nodiscard]] float64 computeProgress() const;
|
[[nodiscard]] float64 computeProgress() const;
|
||||||
|
@ -60,9 +61,13 @@ private:
|
||||||
rpl::variable<DownloadBarProgress> _progress;
|
rpl::variable<DownloadBarProgress> _progress;
|
||||||
Ui::Animations::Simple _finishedAnimation;
|
Ui::Animations::Simple _finishedAnimation;
|
||||||
bool _finished = false;
|
bool _finished = false;
|
||||||
QImage _documentIconOriginal;
|
QImage _documentIconLarge;
|
||||||
QImage _documentIcon;
|
QImage _documentIcon;
|
||||||
QImage _documentIconDone;
|
QImage _documentIconDone;
|
||||||
|
qint64 _thumbnailCacheKey = 0;
|
||||||
|
QImage _thumbnailLarge;
|
||||||
|
QImage _thumbnail;
|
||||||
|
QImage _thumbnailDone;
|
||||||
Text::String _title;
|
Text::String _title;
|
||||||
Text::String _info;
|
Text::String _info;
|
||||||
RadialAnimation _radial;
|
RadialAnimation _radial;
|
||||||
|
|
Loading…
Add table
Reference in a new issue