From 96805b62b247cbadc0bb8e2ab082e49c770c537d Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 23 Aug 2022 20:35:48 +0300 Subject: [PATCH] Seamless switch from strip icons to custom emoji. --- .../chat_helpers/emoji_list_widget.cpp | 22 ++- .../history_view_reactions_selector.cpp | 140 ++++++++++++---- .../history_view_reactions_selector.h | 5 +- .../history_view_reactions_strip.cpp | 95 +++++++---- .../reactions/history_view_reactions_strip.h | 10 ++ .../ui/text/custom_emoji_instance.cpp | 149 +++++++++++------- .../ui/text/custom_emoji_instance.h | 5 + Telegram/lib_ui | 2 +- 8 files changed, 290 insertions(+), 138 deletions(-) diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp index 09097c5cb..e088149da 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp @@ -695,9 +695,7 @@ int EmojiListWidget::countDesiredHeight(int newWidth) { } int EmojiListWidget::defaultMinimalHeight() const { - return (_mode != Mode::Full) - ? st::emojiPanArea.height() - : Inner::defaultMinimalHeight(); + return Inner::defaultMinimalHeight(); } void EmojiListWidget::ensureLoaded(int section) { @@ -961,10 +959,9 @@ void EmojiListWidget::drawRecent( } else { drawEmoji(p, context, position, *emoji); } - } else { - Assert(_recent[index].custom != nullptr); + } else if (const auto custom = _recent[index].custom) { position += _innerPosition + _customPosition; - _recent[index].custom->paint(p, { + const auto paintContext = Ui::Text::CustomEmoji::Context{ .preview = st::windowBgRipple->c, .size = QSize(_customSingleSize, _customSingleSize), .now = now, @@ -972,7 +969,10 @@ void EmojiListWidget::drawRecent( .position = position, .paused = paused, .scaled = context.expanding, - }); + }; + custom->paint(p, paintContext); + } else { + Unexpected("Empty custom emoji in EmojiListWidget::drawRecent."); } } @@ -1597,14 +1597,11 @@ not_null EmojiListWidget::resolveCustomEmoji( } auto repaint = repaintCallback(documentId, RecentEmojiSectionSetId()); auto custom = _customRecentFactory - ? _customRecentFactory(documentId, repaint) - : nullptr; - if (!custom) { - custom = session().data().customEmojiManager().create( + ? _customRecentFactory(documentId, std::move(repaint)) + : session().data().customEmojiManager().create( documentId, std::move(repaint), Data::CustomEmojiManager::SizeTag::Large); - } return _customEmoji.emplace( documentId, CustomEmojiInstance{ .emoji = std::move(custom), .recentOnly = true } @@ -1857,7 +1854,6 @@ QPoint EmojiListWidget::buttonRippleTopLeft(int section) const { void EmojiListWidget::refreshEmoji() { refreshRecent(); refreshCustom(); - resizeToWidth(width()); } void EmojiListWidget::showSet(uint64 setId) { diff --git a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp index d32f09ea6..ef46a2eb9 100644 --- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp +++ b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp @@ -41,10 +41,33 @@ public: QString entityData() override; void paint(QPainter &p, const Context &context) override; void unload() override; + bool ready() override; private: - std::unique_ptr _real; - QPoint _shift; + const std::unique_ptr _real; + const QPoint _shift; + +}; + +class StripEmoji final : public Ui::Text::CustomEmoji { +public: + StripEmoji( + std::unique_ptr wrapped, + not_null 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 _wrapped; + const not_null _strip; + const QPoint _shift; + const int _index = 0; + bool _switched = false; }; @@ -74,6 +97,45 @@ void ShiftedEmoji::unload() { _real->unload(); } +bool ShiftedEmoji::ready() { + return _real->ready(); +} + +StripEmoji::StripEmoji( + std::unique_ptr wrapped, + not_null 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 Selector::Selector( @@ -275,7 +337,6 @@ void Selector::paintCollapsed(QPainter &p) { void Selector::paintExpanding(Painter &p, float64 progress) { const auto rects = paintExpandingBg(p, progress); - //paintStripWithoutExpand(p); progress /= kFullDuration; if (_footer) { _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) { if (progress >= 1.) { return; @@ -365,7 +416,6 @@ void Selector::paintExpanded(QPainter &p) { finishExpand(); } p.drawImage(0, 0, _paintBuffer); - paintStripWithoutExpand(p); } void Selector::finishExpand() { @@ -429,6 +479,9 @@ int Selector::lookupSelectedIndex(QPoint position) const { } void Selector::setSelected(int index) { + if (index >= 0 && _expandScheduled) { + return; + } _strip.setSelected(index); const auto over = (index >= 0); if (_over != over) { @@ -468,6 +521,10 @@ void Selector::mouseReleaseEvent(QMouseEvent *e) { } void Selector::expand() { + if (_expandScheduled) { + return; + } + _expandScheduled = true; const auto parent = parentWidget()->geometry(); const auto additionalBottom = parent.height() - y() - height(); const auto additional = _specialExpandTopSkip + additionalBottom; @@ -483,11 +540,12 @@ void Selector::expand() { cacheExpandIcon(); [[maybe_unused]] const auto grabbed = Ui::GrabWidget(_scroll); + setSelected(-1); - base::call_delayed(kExpandDelay, this, [=] { - _paintBuffer = _cachedRound.PrepareImage(size()); - _expanded = true; + base::call_delayed(kExpandDelay, this, [this] { const auto full = kExpandDuration + kScaleDuration; + _expanded = true; + _paintBuffer = _cachedRound.PrepareImage(size()); _expanding.start([=] { update(); }, 0., full, full); }); } @@ -496,14 +554,7 @@ void Selector::cacheExpandIcon() { _expandIconCache = _cachedRound.PrepareImage({ _size, _size }); _expandIconCache.fill(Qt::transparent); auto q = QPainter(&_expandIconCache); - const auto count = _strip.count(); - _strip.paint( - q, - QPoint(-(count - 1) * _size, 0), - { _size, 0 }, - QRect(-(count - 1) * _size, 0, count * _size, _size), - 1., - false); + _strip.paintOne(q, _strip.count() - 1, { 0, 0 }, 1.); } void Selector::createList(not_null controller) { @@ -511,6 +562,8 @@ void Selector::createList(not_null controller) { auto recent = std::vector(); auto defaultReactionIds = base::flat_map(); recent.reserve(_reactions.recent.size()); + auto index = 0; + const auto inStrip = _strip.count(); for (const auto &reaction : _reactions.recent) { if (const auto id = reaction->id.custom()) { recent.push_back(id); @@ -518,9 +571,12 @@ void Selector::createList(not_null controller) { recent.push_back(reaction->selectAnimation->id); 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 shift = [&] { + _stripPaintOneShift = [&] { // See EmojiListWidget custom emoji position resolving. const auto area = st::emojiPanArea; const auto areaPosition = QPoint( @@ -533,19 +589,34 @@ void Selector::createList(not_null controller) { const auto customSize = Ui::Text::AdjustCustomEmojiSize(esize); const auto customSkip = (esize - customSize) / 2; const auto customPosition = QPoint(customSkip, customSkip); - return QPoint( - (_size - st::reactStripImage) / 2, - (_size - st::reactStripImage) / 2 - ) - areaPosition - innerPosition - customPosition; + return areaPosition + innerPosition + customPosition; }(); - auto factory = [=](DocumentId id, Fn repaint) { - return defaultReactionIds.contains(id) + _defaultReactionShift = QPoint( + (_size - st::reactStripImage) / 2, + (_size - st::reactStripImage) / 2 + ) - _stripPaintOneShift; + auto factory = [=](DocumentId id, Fn repaint) + -> std::unique_ptr { + const auto isDefaultReaction = defaultReactionIds.contains(id); + auto result = isDefaultReaction ? std::make_unique( manager, id, std::move(repaint), - shift) - : nullptr; + _defaultReactionShift) + : manager->create( + id, + std::move(repaint), + Data::CustomEmojiManager::SizeTag::Large); + const auto i = _defaultReactionInStripMap.find(id); + if (i != end(_defaultReactionInStripMap)) { + return std::make_unique( + std::move(result), + &_strip, + -_stripPaintOneShift, + i->second); + } + return result; }; _scroll = Ui::CreateChild(this, st::reactPanelScroll); _scroll->hide(); @@ -631,6 +702,7 @@ void Selector::createList(not_null controller) { 0, 0, })); + _list->setMinimalHeight(geometry.width(), _scroll->height()); updateVisibleTopBottom(); } diff --git a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h index c871bb93a..5b180ab49 100644 --- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h +++ b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h @@ -83,7 +83,6 @@ private: void paintCollapsed(QPainter &p); void paintExpanding(Painter &p, float64 progress); ExpandingRects paintExpandingBg(QPainter &p, float64 progress); - void paintStripWithoutExpand(QPainter &p); void paintFadingExpandIcon(QPainter &p, float64 progress); void paintExpanded(QPainter &p); void paintBubble(QPainter &p, int innerWidth); @@ -100,7 +99,10 @@ private: const base::weak_ptr _parentController; const Data::PossibleItemReactions _reactions; + base::flat_map _defaultReactionInStripMap; Ui::RoundAreaWithShadow _cachedRound; + QPoint _defaultReactionShift; + QPoint _stripPaintOneShift; Strip _strip; rpl::event_stream _chosen; @@ -133,6 +135,7 @@ private: bool _appearing = false; bool _toggling = false; bool _expanded = false; + bool _expandScheduled = false; bool _expandFinished = false; bool _small = false; bool _over = false; diff --git a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_strip.cpp b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_strip.cpp index cced7a9a4..12e402795 100644 --- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_strip.cpp +++ b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_strip.cpp @@ -99,6 +99,28 @@ void Strip::paint( const auto animationRect = clip.marginsRemoved({ 0, skip, 0, skip }); 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 { const auto hoveredSize = int(base::SafeRound(_finalSize * kHoverScale)); const auto basicTargetForScale = [&](int size, float64 scale) { const auto remove = size * (1. - scale) / 2.; @@ -110,7 +132,7 @@ void Strip::paint( )).marginsRemoved({ remove, remove, remove, remove }); }; const auto basicTarget = basicTargetForScale(_finalSize, scale); - const auto countTarget = [&](const ReactionIcons &icon) { + return [=](const ReactionIcons &icon) { const auto selectScale = icon.selectedScale.value( icon.selected ? kHoverScale : 1.); if (selectScale == 1.) { @@ -121,45 +143,62 @@ void Strip::paint( ? basicTargetForScale(_finalSize, finalScale) : 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 animation) { const auto size = int(std::floor(target.width() + 0.01)); const auto frame = animation->frame({ size, size }, _update); 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(); - 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); - } + const auto appear = icon.appear.get(); + if (appear && !icon.appearAnimated && allowAppearStart) { + icon.appearAnimated = true; + appear->animate(_update, 0, appear->framesCount() - 1); } - if (!hiding) { - clearStateForSelectFinished(icon); + if (appear && appear->animating()) { + 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 { return _icons.empty(); } diff --git a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_strip.h b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_strip.h index 22724312e..c607dbf0d 100644 --- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_strip.h +++ b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_strip.h @@ -61,6 +61,8 @@ public: QRect clip, float64 scale, bool hiding); + void paintOne(QPainter &p, int index, QPoint position, float64 scale); + [[nodiscard]] bool inDefaultState(int index) const; [[nodiscard]] bool empty() const; [[nodiscard]] int count() const; @@ -107,6 +109,14 @@ private: [[nodiscard]] bool checkIconLoaded(ReactionDocument &entry) const; void loadIcons(); void checkIcons(); + void paintOne( + QPainter &p, + ReactionIcons &icon, + QPoint position, + QRectF target, + bool allowAppearStart); + [[nodiscard]] Fn resolveCountTargetMethod( + float64 scale) const; void resolveMainReactionIcon(); void setMainReactionIcon(); diff --git a/Telegram/SourceFiles/ui/text/custom_emoji_instance.cpp b/Telegram/SourceFiles/ui/text/custom_emoji_instance.cpp index b4977b5c5..a626ad3ea 100644 --- a/Telegram/SourceFiles/ui/text/custom_emoji_instance.cpp +++ b/Telegram/SourceFiles/ui/text/custom_emoji_instance.cpp @@ -520,6 +520,10 @@ std::unique_ptr Renderer::cancel() { return _loader(); } +bool Renderer::canMakePreview() const { + return _cache.frames() > 0; +} + Preview Renderer::makePreview() const { return _cache.makePreview(); } @@ -567,7 +571,7 @@ void Loading::paint(QPainter &p, const Context &context) { } bool Loading::hasImagePreview() const { -return _preview.isImage(); + return _preview.isImage(); } Preview Loading::imagePreview() const { @@ -599,84 +603,99 @@ Instance::Instance( } QString Instance::entityData() const { - if (const auto loading = std::get_if(&_state)) { - return loading->entityData(); - } else if (const auto caching = std::get_if(&_state)) { - return caching->entityData; - } else if (const auto cached = std::get_if(&_state)) { - return cached->entityData(); - } - Unexpected("State in Instance::entityData."); + return v::match(_state, [](const Loading &state) { + return state.entityData(); + }, [](const Caching &state) { + return state.entityData; + }, [](const Cached &state) { + return state.entityData(); + }); } void Instance::paint(QPainter &p, const Context &context) { - if (const auto loading = std::get_if(&_state)) { - loading->paint(p, context); - loading->load([=](Loader::LoadResult result) { - if (auto caching = std::get_if(&result)) { - caching->renderer->setRepaintCallback([=] { repaint(); }); - _state = std::move(*caching); - } else if (auto cached = std::get_if(&result)) { - _state = std::move(*cached); - repaint(); - } else { - Unexpected("Value in Loader::LoadResult."); - } - }); - } else if (const auto caching = std::get_if(&_state)) { - auto result = caching->renderer->paint(p, context); + v::match(_state, [&](Loading &state) { + state.paint(p, context); + load(state); + }, [&](Caching &state) { + auto result = state.renderer->paint(p, context); if (!result.painted) { - caching->preview.paint(p, context); + state.preview.paint(p, context); } else { - if (!caching->preview.isExactImage()) { - caching->preview = caching->renderer->makePreview(); + if (!state.preview.isExactImage()) { + state.preview = state.renderer->makePreview(); } if (result.next > context.now) { _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); } - } else if (const auto cached = std::get_if(&_state)) { - const auto result = cached->paint(p, context); + }, [&](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(&result)) { + caching->renderer->setRepaintCallback([=] { repaint(); }); + _state = std::move(*caching); + } else if (auto cached = std::get_if(&result)) { + _state = std::move(*cached); + repaint(); + } else { + Unexpected("Value in Loader::LoadResult."); + } + }); } bool Instance::hasImagePreview() const { - if (const auto loading = std::get_if(&_state)) { - return loading->hasImagePreview(); - } else if (const auto caching = std::get_if(&_state)) { - return caching->preview.isImage(); - } else if (const auto cached = std::get_if(&_state)) { + return v::match(_state, [](const Loading &state) { + return state.hasImagePreview(); + }, [](const Caching &state) { + return state.preview.isImage(); + }, [](const Cached &state) { return true; - } - return false; + }); } Preview Instance::imagePreview() const { - if (const auto loading = std::get_if(&_state)) { - return loading->imagePreview(); - } else if (const auto caching = std::get_if(&_state)) { - return caching->preview.isImage() ? caching->preview : Preview(); - } else if (const auto cached = std::get_if(&_state)) { - return cached->makePreview(); - } - return {}; + return v::match(_state, [](const Loading &state) { + return state.imagePreview(); + }, [](const Caching &state) { + return state.preview.isImage() ? state.preview : Preview(); + }, [](const Cached &state) { + return state.makePreview(); + }); } void Instance::updatePreview(Preview preview) { - if (const auto loading = std::get_if(&_state)) { - loading->updatePreview(std::move(preview)); - } else if (const auto caching = std::get_if(&_state)) { - if ((!caching->preview.isImage() && preview.isImage()) - || (!caching->preview && preview)) { - caching->preview = std::move(preview); + v::match(_state, [&](Loading &state) { + state.updatePreview(std::move(preview)); + }, [&](Caching &state) { + if ((!state.preview.isImage() && preview.isImage()) + || (!state.preview && preview)) { + state.preview = std::move(preview); } - } + }, [](const Cached &) {}); } void Instance::repaint() { @@ -694,16 +713,16 @@ void Instance::decrementUsage(not_null object) { if (!_usage.empty()) { return; } - if (const auto loading = std::get_if(&_state)) { - loading->cancel(); - } else if (const auto caching = std::get_if(&_state)) { + v::match(_state, [](Loading &state) { + state.cancel(); + }, [&](Caching &state) { _state = Loading{ - caching->renderer->cancel(), - std::move(caching->preview), + state.renderer->cancel(), + std::move(state.preview), }; - } else if (const auto cached = std::get_if(&_state)) { - _state = cached->unload(); - } + }, [&](Cached &state) { + _state = state.unload(); + }); _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() { _repaint(); } diff --git a/Telegram/SourceFiles/ui/text/custom_emoji_instance.h b/Telegram/SourceFiles/ui/text/custom_emoji_instance.h index 84faae800..832b6683e 100644 --- a/Telegram/SourceFiles/ui/text/custom_emoji_instance.h +++ b/Telegram/SourceFiles/ui/text/custom_emoji_instance.h @@ -145,6 +145,7 @@ public: [[nodiscard]] std::optional ready(const QString &entityData); [[nodiscard]] std::unique_ptr cancel(); + [[nodiscard]] bool canMakePreview() const; [[nodiscard]] Preview makePreview() const; void setRepaintCallback(Fn repaint); @@ -223,6 +224,7 @@ public: [[nodiscard]] QString entityData() const; void paint(QPainter &p, const Context &context); + [[nodiscard]] bool ready(); [[nodiscard]] bool hasImagePreview() const; [[nodiscard]] Preview imagePreview() const; void updatePreview(Preview preview); @@ -233,6 +235,8 @@ public: void repaint(); private: + void load(Loading &state); + std::variant _state; base::flat_set> _usage; Fn that, RepaintRequest)> _repaintLater; @@ -253,6 +257,7 @@ public: QString entityData() override; void paint(QPainter &p, const Context &context) override; void unload() override; + bool ready() override; void repaint(); diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 01c4ba869..fc2c55367 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 01c4ba869a07eabc9eea2b633542a53e9ff6ff4c +Subproject commit fc2c55367099ca7bdeacd0d52ff6007f00a6ba72