Seamless switch from strip icons to custom emoji.

This commit is contained in:
John Preston 2022-08-23 20:35:48 +03:00
parent 4762c7a4fd
commit 96805b62b2
8 changed files with 290 additions and 138 deletions

View File

@ -695,9 +695,7 @@ int EmojiListWidget::countDesiredHeight(int newWidth) {
} }
int EmojiListWidget::defaultMinimalHeight() const { int EmojiListWidget::defaultMinimalHeight() const {
return (_mode != Mode::Full) return Inner::defaultMinimalHeight();
? st::emojiPanArea.height()
: Inner::defaultMinimalHeight();
} }
void EmojiListWidget::ensureLoaded(int section) { void EmojiListWidget::ensureLoaded(int section) {
@ -961,10 +959,9 @@ void EmojiListWidget::drawRecent(
} else { } else {
drawEmoji(p, context, position, *emoji); drawEmoji(p, context, position, *emoji);
} }
} else { } else if (const auto custom = _recent[index].custom) {
Assert(_recent[index].custom != nullptr);
position += _innerPosition + _customPosition; position += _innerPosition + _customPosition;
_recent[index].custom->paint(p, { const auto paintContext = Ui::Text::CustomEmoji::Context{
.preview = st::windowBgRipple->c, .preview = st::windowBgRipple->c,
.size = QSize(_customSingleSize, _customSingleSize), .size = QSize(_customSingleSize, _customSingleSize),
.now = now, .now = now,
@ -972,7 +969,10 @@ void EmojiListWidget::drawRecent(
.position = position, .position = position,
.paused = paused, .paused = paused,
.scaled = context.expanding, .scaled = context.expanding,
}); };
custom->paint(p, paintContext);
} else {
Unexpected("Empty custom emoji in EmojiListWidget::drawRecent.");
} }
} }
@ -1597,14 +1597,11 @@ not_null<Ui::Text::CustomEmoji*> EmojiListWidget::resolveCustomEmoji(
} }
auto repaint = repaintCallback(documentId, RecentEmojiSectionSetId()); auto repaint = repaintCallback(documentId, RecentEmojiSectionSetId());
auto custom = _customRecentFactory auto custom = _customRecentFactory
? _customRecentFactory(documentId, repaint) ? _customRecentFactory(documentId, std::move(repaint))
: nullptr; : session().data().customEmojiManager().create(
if (!custom) {
custom = session().data().customEmojiManager().create(
documentId, documentId,
std::move(repaint), std::move(repaint),
Data::CustomEmojiManager::SizeTag::Large); Data::CustomEmojiManager::SizeTag::Large);
}
return _customEmoji.emplace( return _customEmoji.emplace(
documentId, documentId,
CustomEmojiInstance{ .emoji = std::move(custom), .recentOnly = true } CustomEmojiInstance{ .emoji = std::move(custom), .recentOnly = true }
@ -1857,7 +1854,6 @@ QPoint EmojiListWidget::buttonRippleTopLeft(int section) const {
void EmojiListWidget::refreshEmoji() { void EmojiListWidget::refreshEmoji() {
refreshRecent(); refreshRecent();
refreshCustom(); refreshCustom();
resizeToWidth(width());
} }
void EmojiListWidget::showSet(uint64 setId) { void EmojiListWidget::showSet(uint64 setId) {

View File

@ -41,10 +41,33 @@ public:
QString entityData() override; QString entityData() override;
void paint(QPainter &p, const Context &context) override; void paint(QPainter &p, const Context &context) override;
void unload() override; void unload() override;
bool ready() override;
private: private:
std::unique_ptr<Ui::Text::CustomEmoji> _real; const std::unique_ptr<Ui::Text::CustomEmoji> _real;
QPoint _shift; const QPoint _shift;
};
class StripEmoji final : public Ui::Text::CustomEmoji {
public:
StripEmoji(
std::unique_ptr<Ui::Text::CustomEmoji> wrapped,
not_null<Strip*> strip,
QPoint shift,
int index);
QString entityData() override;
void paint(QPainter &p, const Context &context) override;
void unload() override;
bool ready() override;
private:
const std::unique_ptr<Ui::Text::CustomEmoji> _wrapped;
const not_null<Strip*> _strip;
const QPoint _shift;
const int _index = 0;
bool _switched = false;
}; };
@ -74,6 +97,45 @@ void ShiftedEmoji::unload() {
_real->unload(); _real->unload();
} }
bool ShiftedEmoji::ready() {
return _real->ready();
}
StripEmoji::StripEmoji(
std::unique_ptr<Ui::Text::CustomEmoji> wrapped,
not_null<Strip*> strip,
QPoint shift,
int index)
: _wrapped(std::move(wrapped))
, _strip(strip)
, _shift(shift)
, _index(index) {
}
QString StripEmoji::entityData() {
return _wrapped->entityData();
}
void StripEmoji::paint(QPainter &p, const Context &context) {
if (_switched) {
_wrapped->paint(p, context);
} else if (_wrapped->ready() && _strip->inDefaultState(_index)) {
_switched = true;
_wrapped->paint(p, context);
} else {
_strip->paintOne(p, _index, context.position + _shift, 1.);
}
}
void StripEmoji::unload() {
_wrapped->unload();
_switched = true;
}
bool StripEmoji::ready() {
return _wrapped->ready();
}
} // namespace } // namespace
Selector::Selector( Selector::Selector(
@ -275,7 +337,6 @@ void Selector::paintCollapsed(QPainter &p) {
void Selector::paintExpanding(Painter &p, float64 progress) { void Selector::paintExpanding(Painter &p, float64 progress) {
const auto rects = paintExpandingBg(p, progress); const auto rects = paintExpandingBg(p, progress);
//paintStripWithoutExpand(p);
progress /= kFullDuration; progress /= kFullDuration;
if (_footer) { if (_footer) {
_footer->paintExpanding( _footer->paintExpanding(
@ -334,16 +395,6 @@ auto Selector::paintExpandingBg(QPainter &p, float64 progress)
}; };
} }
void Selector::paintStripWithoutExpand(QPainter &p) {
_strip.paint(
p,
_inner.topLeft() + QPoint(_skipx, _skipy),
{ _size, 0 },
_inner.marginsRemoved({ 0, 0, _skipx + _size, 0 }),
1.,
false);
}
void Selector::paintFadingExpandIcon(QPainter &p, float64 progress) { void Selector::paintFadingExpandIcon(QPainter &p, float64 progress) {
if (progress >= 1.) { if (progress >= 1.) {
return; return;
@ -365,7 +416,6 @@ void Selector::paintExpanded(QPainter &p) {
finishExpand(); finishExpand();
} }
p.drawImage(0, 0, _paintBuffer); p.drawImage(0, 0, _paintBuffer);
paintStripWithoutExpand(p);
} }
void Selector::finishExpand() { void Selector::finishExpand() {
@ -429,6 +479,9 @@ int Selector::lookupSelectedIndex(QPoint position) const {
} }
void Selector::setSelected(int index) { void Selector::setSelected(int index) {
if (index >= 0 && _expandScheduled) {
return;
}
_strip.setSelected(index); _strip.setSelected(index);
const auto over = (index >= 0); const auto over = (index >= 0);
if (_over != over) { if (_over != over) {
@ -468,6 +521,10 @@ void Selector::mouseReleaseEvent(QMouseEvent *e) {
} }
void Selector::expand() { void Selector::expand() {
if (_expandScheduled) {
return;
}
_expandScheduled = true;
const auto parent = parentWidget()->geometry(); const auto parent = parentWidget()->geometry();
const auto additionalBottom = parent.height() - y() - height(); const auto additionalBottom = parent.height() - y() - height();
const auto additional = _specialExpandTopSkip + additionalBottom; const auto additional = _specialExpandTopSkip + additionalBottom;
@ -483,11 +540,12 @@ void Selector::expand() {
cacheExpandIcon(); cacheExpandIcon();
[[maybe_unused]] const auto grabbed = Ui::GrabWidget(_scroll); [[maybe_unused]] const auto grabbed = Ui::GrabWidget(_scroll);
setSelected(-1);
base::call_delayed(kExpandDelay, this, [=] { base::call_delayed(kExpandDelay, this, [this] {
_paintBuffer = _cachedRound.PrepareImage(size());
_expanded = true;
const auto full = kExpandDuration + kScaleDuration; const auto full = kExpandDuration + kScaleDuration;
_expanded = true;
_paintBuffer = _cachedRound.PrepareImage(size());
_expanding.start([=] { update(); }, 0., full, full); _expanding.start([=] { update(); }, 0., full, full);
}); });
} }
@ -496,14 +554,7 @@ void Selector::cacheExpandIcon() {
_expandIconCache = _cachedRound.PrepareImage({ _size, _size }); _expandIconCache = _cachedRound.PrepareImage({ _size, _size });
_expandIconCache.fill(Qt::transparent); _expandIconCache.fill(Qt::transparent);
auto q = QPainter(&_expandIconCache); auto q = QPainter(&_expandIconCache);
const auto count = _strip.count(); _strip.paintOne(q, _strip.count() - 1, { 0, 0 }, 1.);
_strip.paint(
q,
QPoint(-(count - 1) * _size, 0),
{ _size, 0 },
QRect(-(count - 1) * _size, 0, count * _size, _size),
1.,
false);
} }
void Selector::createList(not_null<Window::SessionController*> controller) { void Selector::createList(not_null<Window::SessionController*> controller) {
@ -511,6 +562,8 @@ void Selector::createList(not_null<Window::SessionController*> controller) {
auto recent = std::vector<DocumentId>(); auto recent = std::vector<DocumentId>();
auto defaultReactionIds = base::flat_map<DocumentId, QString>(); auto defaultReactionIds = base::flat_map<DocumentId, QString>();
recent.reserve(_reactions.recent.size()); recent.reserve(_reactions.recent.size());
auto index = 0;
const auto inStrip = _strip.count();
for (const auto &reaction : _reactions.recent) { for (const auto &reaction : _reactions.recent) {
if (const auto id = reaction->id.custom()) { if (const auto id = reaction->id.custom()) {
recent.push_back(id); recent.push_back(id);
@ -518,9 +571,12 @@ void Selector::createList(not_null<Window::SessionController*> controller) {
recent.push_back(reaction->selectAnimation->id); recent.push_back(reaction->selectAnimation->id);
defaultReactionIds.emplace(recent.back(), reaction->id.emoji()); defaultReactionIds.emplace(recent.back(), reaction->id.emoji());
} }
if (index + 1 < inStrip) {
_defaultReactionInStripMap.emplace(recent.back(), index++);
}
}; };
const auto manager = &controller->session().data().customEmojiManager(); const auto manager = &controller->session().data().customEmojiManager();
const auto shift = [&] { _stripPaintOneShift = [&] {
// See EmojiListWidget custom emoji position resolving. // See EmojiListWidget custom emoji position resolving.
const auto area = st::emojiPanArea; const auto area = st::emojiPanArea;
const auto areaPosition = QPoint( const auto areaPosition = QPoint(
@ -533,19 +589,34 @@ void Selector::createList(not_null<Window::SessionController*> controller) {
const auto customSize = Ui::Text::AdjustCustomEmojiSize(esize); const auto customSize = Ui::Text::AdjustCustomEmojiSize(esize);
const auto customSkip = (esize - customSize) / 2; const auto customSkip = (esize - customSize) / 2;
const auto customPosition = QPoint(customSkip, customSkip); const auto customPosition = QPoint(customSkip, customSkip);
return QPoint( return areaPosition + innerPosition + customPosition;
(_size - st::reactStripImage) / 2,
(_size - st::reactStripImage) / 2
) - areaPosition - innerPosition - customPosition;
}(); }();
auto factory = [=](DocumentId id, Fn<void()> repaint) { _defaultReactionShift = QPoint(
return defaultReactionIds.contains(id) (_size - st::reactStripImage) / 2,
(_size - st::reactStripImage) / 2
) - _stripPaintOneShift;
auto factory = [=](DocumentId id, Fn<void()> repaint)
-> std::unique_ptr<Ui::Text::CustomEmoji> {
const auto isDefaultReaction = defaultReactionIds.contains(id);
auto result = isDefaultReaction
? std::make_unique<ShiftedEmoji>( ? std::make_unique<ShiftedEmoji>(
manager, manager,
id, id,
std::move(repaint), std::move(repaint),
shift) _defaultReactionShift)
: nullptr; : manager->create(
id,
std::move(repaint),
Data::CustomEmojiManager::SizeTag::Large);
const auto i = _defaultReactionInStripMap.find(id);
if (i != end(_defaultReactionInStripMap)) {
return std::make_unique<StripEmoji>(
std::move(result),
&_strip,
-_stripPaintOneShift,
i->second);
}
return result;
}; };
_scroll = Ui::CreateChild<Ui::ScrollArea>(this, st::reactPanelScroll); _scroll = Ui::CreateChild<Ui::ScrollArea>(this, st::reactPanelScroll);
_scroll->hide(); _scroll->hide();
@ -631,6 +702,7 @@ void Selector::createList(not_null<Window::SessionController*> controller) {
0, 0,
0, 0,
})); }));
_list->setMinimalHeight(geometry.width(), _scroll->height());
updateVisibleTopBottom(); updateVisibleTopBottom();
} }

View File

@ -83,7 +83,6 @@ private:
void paintCollapsed(QPainter &p); void paintCollapsed(QPainter &p);
void paintExpanding(Painter &p, float64 progress); void paintExpanding(Painter &p, float64 progress);
ExpandingRects paintExpandingBg(QPainter &p, float64 progress); ExpandingRects paintExpandingBg(QPainter &p, float64 progress);
void paintStripWithoutExpand(QPainter &p);
void paintFadingExpandIcon(QPainter &p, float64 progress); void paintFadingExpandIcon(QPainter &p, float64 progress);
void paintExpanded(QPainter &p); void paintExpanded(QPainter &p);
void paintBubble(QPainter &p, int innerWidth); void paintBubble(QPainter &p, int innerWidth);
@ -100,7 +99,10 @@ private:
const base::weak_ptr<Window::SessionController> _parentController; const base::weak_ptr<Window::SessionController> _parentController;
const Data::PossibleItemReactions _reactions; const Data::PossibleItemReactions _reactions;
base::flat_map<DocumentId, int> _defaultReactionInStripMap;
Ui::RoundAreaWithShadow _cachedRound; Ui::RoundAreaWithShadow _cachedRound;
QPoint _defaultReactionShift;
QPoint _stripPaintOneShift;
Strip _strip; Strip _strip;
rpl::event_stream<ChosenReaction> _chosen; rpl::event_stream<ChosenReaction> _chosen;
@ -133,6 +135,7 @@ private:
bool _appearing = false; bool _appearing = false;
bool _toggling = false; bool _toggling = false;
bool _expanded = false; bool _expanded = false;
bool _expandScheduled = false;
bool _expandFinished = false; bool _expandFinished = false;
bool _small = false; bool _small = false;
bool _over = false; bool _over = false;

View File

@ -99,6 +99,28 @@ void Strip::paint(
const auto animationRect = clip.marginsRemoved({ 0, skip, 0, skip }); const auto animationRect = clip.marginsRemoved({ 0, skip, 0, skip });
PainterHighQualityEnabler hq(p); PainterHighQualityEnabler hq(p);
const auto countTarget = resolveCountTargetMethod(scale);
for (auto &icon : _icons) {
const auto target = countTarget(icon).translated(position);
position += shift;
if (target.intersects(clip)) {
paintOne(
p,
icon,
position - shift,
target,
!hiding && target.intersects(animationRect));
} else if (!hiding) {
clearStateForHidden(icon);
}
if (!hiding) {
clearStateForSelectFinished(icon);
}
}
}
auto Strip::resolveCountTargetMethod(float64 scale) const
-> Fn<QRectF(const ReactionIcons&)> {
const auto hoveredSize = int(base::SafeRound(_finalSize * kHoverScale)); const auto hoveredSize = int(base::SafeRound(_finalSize * kHoverScale));
const auto basicTargetForScale = [&](int size, float64 scale) { const auto basicTargetForScale = [&](int size, float64 scale) {
const auto remove = size * (1. - scale) / 2.; const auto remove = size * (1. - scale) / 2.;
@ -110,7 +132,7 @@ void Strip::paint(
)).marginsRemoved({ remove, remove, remove, remove }); )).marginsRemoved({ remove, remove, remove, remove });
}; };
const auto basicTarget = basicTargetForScale(_finalSize, scale); const auto basicTarget = basicTargetForScale(_finalSize, scale);
const auto countTarget = [&](const ReactionIcons &icon) { return [=](const ReactionIcons &icon) {
const auto selectScale = icon.selectedScale.value( const auto selectScale = icon.selectedScale.value(
icon.selected ? kHoverScale : 1.); icon.selected ? kHoverScale : 1.);
if (selectScale == 1.) { if (selectScale == 1.) {
@ -121,45 +143,62 @@ void Strip::paint(
? basicTargetForScale(_finalSize, finalScale) ? basicTargetForScale(_finalSize, finalScale)
: basicTargetForScale(hoveredSize, finalScale / kHoverScale); : basicTargetForScale(hoveredSize, finalScale / kHoverScale);
}; };
for (auto &icon : _icons) { }
const auto target = countTarget(icon).translated(position);
position += shift;
void Strip::paintOne(
QPainter &p,
ReactionIcons &icon,
QPoint position,
QRectF target,
bool allowAppearStart) {
if (icon.added == AddedButton::Premium) {
paintPremiumIcon(p, position, target);
} else if (icon.added == AddedButton::Expand) {
paintExpandIcon(p, position, target);
} else {
const auto paintFrame = [&](not_null<Lottie::Icon*> animation) { const auto paintFrame = [&](not_null<Lottie::Icon*> animation) {
const auto size = int(std::floor(target.width() + 0.01)); const auto size = int(std::floor(target.width() + 0.01));
const auto frame = animation->frame({ size, size }, _update); const auto frame = animation->frame({ size, size }, _update);
p.drawImage(target, frame.image); p.drawImage(target, frame.image);
}; };
if (!target.intersects(clip)) { const auto appear = icon.appear.get();
if (!hiding) { if (appear && !icon.appearAnimated && allowAppearStart) {
clearStateForHidden(icon); icon.appearAnimated = true;
} appear->animate(_update, 0, appear->framesCount() - 1);
} else if (icon.added == AddedButton::Premium) {
paintPremiumIcon(p, position - shift, target);
} else if (icon.added == AddedButton::Expand) {
paintExpandIcon(p, position - shift, target);
} else {
const auto appear = icon.appear.get();
if (!hiding
&& appear
&& !icon.appearAnimated
&& target.intersects(animationRect)) {
icon.appearAnimated = true;
appear->animate(_update, 0, appear->framesCount() - 1);
}
if (appear && appear->animating()) {
paintFrame(appear);
} else if (const auto select = icon.select.get()) {
paintFrame(select);
}
} }
if (!hiding) { if (appear && appear->animating()) {
clearStateForSelectFinished(icon); paintFrame(appear);
} else if (const auto select = icon.select.get()) {
paintFrame(select);
} }
} }
} }
void Strip::paintOne(
QPainter &p,
int index,
QPoint position,
float64 scale) {
Expects(index >= 0 && index < _icons.size());
auto &icon = _icons[index];
const auto countTarget = resolveCountTargetMethod(scale);
const auto target = countTarget(icon).translated(position);
paintOne(p, icon, position, target, false);
}
bool Strip::inDefaultState(int index) const {
Expects(index >= 0 && index < _icons.size());
const auto &icon = _icons[index];
return !icon.selected
&& !icon.selectedScale.animating()
&& icon.select
&& !icon.select->animating()
&& (!icon.appear || !icon.appear->animating());
}
bool Strip::empty() const { bool Strip::empty() const {
return _icons.empty(); return _icons.empty();
} }

View File

@ -61,6 +61,8 @@ public:
QRect clip, QRect clip,
float64 scale, float64 scale,
bool hiding); bool hiding);
void paintOne(QPainter &p, int index, QPoint position, float64 scale);
[[nodiscard]] bool inDefaultState(int index) const;
[[nodiscard]] bool empty() const; [[nodiscard]] bool empty() const;
[[nodiscard]] int count() const; [[nodiscard]] int count() const;
@ -107,6 +109,14 @@ private:
[[nodiscard]] bool checkIconLoaded(ReactionDocument &entry) const; [[nodiscard]] bool checkIconLoaded(ReactionDocument &entry) const;
void loadIcons(); void loadIcons();
void checkIcons(); void checkIcons();
void paintOne(
QPainter &p,
ReactionIcons &icon,
QPoint position,
QRectF target,
bool allowAppearStart);
[[nodiscard]] Fn<QRectF(const ReactionIcons&)> resolveCountTargetMethod(
float64 scale) const;
void resolveMainReactionIcon(); void resolveMainReactionIcon();
void setMainReactionIcon(); void setMainReactionIcon();

View File

@ -520,6 +520,10 @@ std::unique_ptr<Loader> Renderer::cancel() {
return _loader(); return _loader();
} }
bool Renderer::canMakePreview() const {
return _cache.frames() > 0;
}
Preview Renderer::makePreview() const { Preview Renderer::makePreview() const {
return _cache.makePreview(); return _cache.makePreview();
} }
@ -567,7 +571,7 @@ void Loading::paint(QPainter &p, const Context &context) {
} }
bool Loading::hasImagePreview() const { bool Loading::hasImagePreview() const {
return _preview.isImage(); return _preview.isImage();
} }
Preview Loading::imagePreview() const { Preview Loading::imagePreview() const {
@ -599,84 +603,99 @@ Instance::Instance(
} }
QString Instance::entityData() const { QString Instance::entityData() const {
if (const auto loading = std::get_if<Loading>(&_state)) { return v::match(_state, [](const Loading &state) {
return loading->entityData(); return state.entityData();
} else if (const auto caching = std::get_if<Caching>(&_state)) { }, [](const Caching &state) {
return caching->entityData; return state.entityData;
} else if (const auto cached = std::get_if<Cached>(&_state)) { }, [](const Cached &state) {
return cached->entityData(); return state.entityData();
} });
Unexpected("State in Instance::entityData.");
} }
void Instance::paint(QPainter &p, const Context &context) { void Instance::paint(QPainter &p, const Context &context) {
if (const auto loading = std::get_if<Loading>(&_state)) { v::match(_state, [&](Loading &state) {
loading->paint(p, context); state.paint(p, context);
loading->load([=](Loader::LoadResult result) { load(state);
if (auto caching = std::get_if<Caching>(&result)) { }, [&](Caching &state) {
caching->renderer->setRepaintCallback([=] { repaint(); }); auto result = state.renderer->paint(p, context);
_state = std::move(*caching);
} else if (auto cached = std::get_if<Cached>(&result)) {
_state = std::move(*cached);
repaint();
} else {
Unexpected("Value in Loader::LoadResult.");
}
});
} else if (const auto caching = std::get_if<Caching>(&_state)) {
auto result = caching->renderer->paint(p, context);
if (!result.painted) { if (!result.painted) {
caching->preview.paint(p, context); state.preview.paint(p, context);
} else { } else {
if (!caching->preview.isExactImage()) { if (!state.preview.isExactImage()) {
caching->preview = caching->renderer->makePreview(); state.preview = state.renderer->makePreview();
} }
if (result.next > context.now) { if (result.next > context.now) {
_repaintLater(this, { result.next, result.duration }); _repaintLater(this, { result.next, result.duration });
} }
} }
if (auto cached = caching->renderer->ready(caching->entityData)) { if (auto cached = state.renderer->ready(state.entityData)) {
_state = std::move(*cached); _state = std::move(*cached);
} }
} else if (const auto cached = std::get_if<Cached>(&_state)) { }, [&](Cached &state) {
const auto result = cached->paint(p, context); const auto result = state.paint(p, context);
if (result.next > context.now) { if (result.next > context.now) {
_repaintLater(this, { result.next, result.duration }); _repaintLater(this, { result.next, result.duration });
} }
} });
}
bool Instance::ready() {
return v::match(_state, [&](Loading &state) {
if (state.hasImagePreview()) {
return true;
}
load(state);
return false;
}, [](Caching &state) {
return state.renderer->canMakePreview();
}, [](Cached &state) {
return true;
});
}
void Instance::load(Loading &state) {
state.load([=](Loader::LoadResult result) {
if (auto caching = std::get_if<Caching>(&result)) {
caching->renderer->setRepaintCallback([=] { repaint(); });
_state = std::move(*caching);
} else if (auto cached = std::get_if<Cached>(&result)) {
_state = std::move(*cached);
repaint();
} else {
Unexpected("Value in Loader::LoadResult.");
}
});
} }
bool Instance::hasImagePreview() const { bool Instance::hasImagePreview() const {
if (const auto loading = std::get_if<Loading>(&_state)) { return v::match(_state, [](const Loading &state) {
return loading->hasImagePreview(); return state.hasImagePreview();
} else if (const auto caching = std::get_if<Caching>(&_state)) { }, [](const Caching &state) {
return caching->preview.isImage(); return state.preview.isImage();
} else if (const auto cached = std::get_if<Cached>(&_state)) { }, [](const Cached &state) {
return true; return true;
} });
return false;
} }
Preview Instance::imagePreview() const { Preview Instance::imagePreview() const {
if (const auto loading = std::get_if<Loading>(&_state)) { return v::match(_state, [](const Loading &state) {
return loading->imagePreview(); return state.imagePreview();
} else if (const auto caching = std::get_if<Caching>(&_state)) { }, [](const Caching &state) {
return caching->preview.isImage() ? caching->preview : Preview(); return state.preview.isImage() ? state.preview : Preview();
} else if (const auto cached = std::get_if<Cached>(&_state)) { }, [](const Cached &state) {
return cached->makePreview(); return state.makePreview();
} });
return {};
} }
void Instance::updatePreview(Preview preview) { void Instance::updatePreview(Preview preview) {
if (const auto loading = std::get_if<Loading>(&_state)) { v::match(_state, [&](Loading &state) {
loading->updatePreview(std::move(preview)); state.updatePreview(std::move(preview));
} else if (const auto caching = std::get_if<Caching>(&_state)) { }, [&](Caching &state) {
if ((!caching->preview.isImage() && preview.isImage()) if ((!state.preview.isImage() && preview.isImage())
|| (!caching->preview && preview)) { || (!state.preview && preview)) {
caching->preview = std::move(preview); state.preview = std::move(preview);
} }
} }, [](const Cached &) {});
} }
void Instance::repaint() { void Instance::repaint() {
@ -694,16 +713,16 @@ void Instance::decrementUsage(not_null<Object*> object) {
if (!_usage.empty()) { if (!_usage.empty()) {
return; return;
} }
if (const auto loading = std::get_if<Loading>(&_state)) { v::match(_state, [](Loading &state) {
loading->cancel(); state.cancel();
} else if (const auto caching = std::get_if<Caching>(&_state)) { }, [&](Caching &state) {
_state = Loading{ _state = Loading{
caching->renderer->cancel(), state.renderer->cancel(),
std::move(caching->preview), std::move(state.preview),
}; };
} else if (const auto cached = std::get_if<Cached>(&_state)) { }, [&](Cached &state) {
_state = cached->unload(); _state = state.unload();
} });
_repaintLater(this, RepaintRequest()); _repaintLater(this, RepaintRequest());
} }
@ -735,6 +754,14 @@ void Object::unload() {
} }
} }
bool Object::ready() {
if (!_using) {
_using = true;
_instance->incrementUsage(this);
}
return _instance->ready();
}
void Object::repaint() { void Object::repaint() {
_repaint(); _repaint();
} }

View File

@ -145,6 +145,7 @@ public:
[[nodiscard]] std::optional<Cached> ready(const QString &entityData); [[nodiscard]] std::optional<Cached> ready(const QString &entityData);
[[nodiscard]] std::unique_ptr<Loader> cancel(); [[nodiscard]] std::unique_ptr<Loader> cancel();
[[nodiscard]] bool canMakePreview() const;
[[nodiscard]] Preview makePreview() const; [[nodiscard]] Preview makePreview() const;
void setRepaintCallback(Fn<void()> repaint); void setRepaintCallback(Fn<void()> repaint);
@ -223,6 +224,7 @@ public:
[[nodiscard]] QString entityData() const; [[nodiscard]] QString entityData() const;
void paint(QPainter &p, const Context &context); void paint(QPainter &p, const Context &context);
[[nodiscard]] bool ready();
[[nodiscard]] bool hasImagePreview() const; [[nodiscard]] bool hasImagePreview() const;
[[nodiscard]] Preview imagePreview() const; [[nodiscard]] Preview imagePreview() const;
void updatePreview(Preview preview); void updatePreview(Preview preview);
@ -233,6 +235,8 @@ public:
void repaint(); void repaint();
private: private:
void load(Loading &state);
std::variant<Loading, Caching, Cached> _state; std::variant<Loading, Caching, Cached> _state;
base::flat_set<not_null<Object*>> _usage; base::flat_set<not_null<Object*>> _usage;
Fn<void(not_null<Instance*> that, RepaintRequest)> _repaintLater; Fn<void(not_null<Instance*> that, RepaintRequest)> _repaintLater;
@ -253,6 +257,7 @@ public:
QString entityData() override; QString entityData() override;
void paint(QPainter &p, const Context &context) override; void paint(QPainter &p, const Context &context) override;
void unload() override; void unload() override;
bool ready() override;
void repaint(); void repaint();

@ -1 +1 @@
Subproject commit 01c4ba869a07eabc9eea2b633542a53e9ff6ff4c Subproject commit fc2c55367099ca7bdeacd0d52ff6007f00a6ba72