Seamless switch from strip icons to custom emoji.
This commit is contained in:
parent
4762c7a4fd
commit
96805b62b2
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
}();
|
||||||
|
_defaultReactionShift = QPoint(
|
||||||
(_size - st::reactStripImage) / 2,
|
(_size - st::reactStripImage) / 2,
|
||||||
(_size - st::reactStripImage) / 2
|
(_size - st::reactStripImage) / 2
|
||||||
) - areaPosition - innerPosition - customPosition;
|
) - _stripPaintOneShift;
|
||||||
}();
|
auto factory = [=](DocumentId id, Fn<void()> repaint)
|
||||||
auto factory = [=](DocumentId id, Fn<void()> repaint) {
|
-> std::unique_ptr<Ui::Text::CustomEmoji> {
|
||||||
return defaultReactionIds.contains(id)
|
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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,30 +143,27 @@ 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)) {
|
|
||||||
if (!hiding) {
|
|
||||||
clearStateForHidden(icon);
|
|
||||||
}
|
|
||||||
} 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();
|
const auto appear = icon.appear.get();
|
||||||
if (!hiding
|
if (appear && !icon.appearAnimated && allowAppearStart) {
|
||||||
&& appear
|
|
||||||
&& !icon.appearAnimated
|
|
||||||
&& target.intersects(animationRect)) {
|
|
||||||
icon.appearAnimated = true;
|
icon.appearAnimated = true;
|
||||||
appear->animate(_update, 0, appear->framesCount() - 1);
|
appear->animate(_update, 0, appear->framesCount() - 1);
|
||||||
}
|
}
|
||||||
|
@ -154,10 +173,30 @@ void Strip::paint(
|
||||||
paintFrame(select);
|
paintFrame(select);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!hiding) {
|
}
|
||||||
clearStateForSelectFinished(icon);
|
|
||||||
}
|
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 {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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,20 +603,58 @@ 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);
|
||||||
|
}, [&](Caching &state) {
|
||||||
|
auto result = state.renderer->paint(p, context);
|
||||||
|
if (!result.painted) {
|
||||||
|
state.preview.paint(p, context);
|
||||||
|
} else {
|
||||||
|
if (!state.preview.isExactImage()) {
|
||||||
|
state.preview = state.renderer->makePreview();
|
||||||
|
}
|
||||||
|
if (result.next > context.now) {
|
||||||
|
_repaintLater(this, { result.next, result.duration });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (auto cached = state.renderer->ready(state.entityData)) {
|
||||||
|
_state = std::move(*cached);
|
||||||
|
}
|
||||||
|
}, [&](Cached &state) {
|
||||||
|
const auto result = state.paint(p, context);
|
||||||
|
if (result.next > context.now) {
|
||||||
|
_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)) {
|
if (auto caching = std::get_if<Caching>(&result)) {
|
||||||
caching->renderer->setRepaintCallback([=] { repaint(); });
|
caching->renderer->setRepaintCallback([=] { repaint(); });
|
||||||
_state = std::move(*caching);
|
_state = std::move(*caching);
|
||||||
|
@ -623,60 +665,37 @@ void Instance::paint(QPainter &p, const Context &context) {
|
||||||
Unexpected("Value in Loader::LoadResult.");
|
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) {
|
|
||||||
caching->preview.paint(p, context);
|
|
||||||
} else {
|
|
||||||
if (!caching->preview.isExactImage()) {
|
|
||||||
caching->preview = caching->renderer->makePreview();
|
|
||||||
}
|
|
||||||
if (result.next > context.now) {
|
|
||||||
_repaintLater(this, { result.next, result.duration });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (auto cached = caching->renderer->ready(caching->entityData)) {
|
|
||||||
_state = std::move(*cached);
|
|
||||||
}
|
|
||||||
} else if (const auto cached = std::get_if<Cached>(&_state)) {
|
|
||||||
const auto result = cached->paint(p, context);
|
|
||||||
if (result.next > context.now) {
|
|
||||||
_repaintLater(this, { result.next, result.duration });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
Loading…
Reference in New Issue