From 126fd89bb7d5d16a2124459cc866e3ecc694b8ad Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 2 Aug 2024 15:21:39 +0200 Subject: [PATCH] Remove accidentally added files from git. --- .../_history_view_reactions_button.cpp | 1422 ----------------- .../_history_view_reactions_button.h | 373 ----- 2 files changed, 1795 deletions(-) delete mode 100644 Telegram/SourceFiles/history/view/reactions/_history_view_reactions_button.cpp delete mode 100644 Telegram/SourceFiles/history/view/reactions/_history_view_reactions_button.h diff --git a/Telegram/SourceFiles/history/view/reactions/_history_view_reactions_button.cpp b/Telegram/SourceFiles/history/view/reactions/_history_view_reactions_button.cpp deleted file mode 100644 index e4578c7b5..000000000 --- a/Telegram/SourceFiles/history/view/reactions/_history_view_reactions_button.cpp +++ /dev/null @@ -1,1422 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include "history/view/history_view_react_button.h" - -#include "history/view/history_view_cursor_state.h" -#include "history/history_item.h" -#include "ui/chat/chat_style.h" -#include "ui/chat/message_bubble.h" -#include "ui/widgets/popup_menu.h" -#include "data/data_message_reactions.h" -#include "data/data_session.h" -#include "data/data_document.h" -#include "data/data_document_media.h" -#include "data/data_peer_values.h" -#include "lang/lang_keys.h" -#include "core/click_handler_types.h" -#include "lottie/lottie_icon.h" -#include "main/main_session.h" -#include "base/event_filter.h" -#include "styles/style_chat.h" -#include "styles/style_menu_icons.h" - -namespace HistoryView::Reactions { -namespace { - -constexpr auto kDivider = 4; -constexpr auto kToggleDuration = crl::time(120); -constexpr auto kActivateDuration = crl::time(150); -constexpr auto kExpandDuration = crl::time(300); -constexpr auto kCollapseDuration = crl::time(250); -constexpr auto kEmojiCacheIndex = 0; -constexpr auto kButtonShowDelay = crl::time(300); -constexpr auto kButtonExpandDelay = crl::time(25); -constexpr auto kButtonHideDelay = crl::time(300); -constexpr auto kButtonExpandedHideDelay = crl::time(0); -constexpr auto kSizeForDownscale = 96; -constexpr auto kHoverScaleDuration = crl::time(200); -constexpr auto kHoverScale = 1.24; -constexpr auto kMaxReactionsScrollAtOnce = 2; - -[[nodiscard]] QPoint LocalPosition(not_null<QWheelEvent*> e) { -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - return e->position().toPoint(); -#else // Qt >= 6.0 - return e->pos(); -#endif // Qt >= 6.0 -} - -[[nodiscard]] QSize CountMaxSizeWithMargins(style::margins margins) { - return QRect( - QPoint(), - st::reactionCornerSize - ).marginsAdded(margins).size(); -} - -[[nodiscard]] QSize CountOuterSize() { - return CountMaxSizeWithMargins(st::reactionCornerShadow); -} - -[[nodiscard]] int CornerImageSize(float64 scale) { - return int(base::SafeRound(st::reactionCornerImage * scale)); -} - -[[nodiscard]] int MainReactionSize() { - return style::ConvertScale(kSizeForDownscale); -} - -[[nodiscard]] std::shared_ptr<Lottie::Icon> CreateIcon( - not_null<Data::DocumentMedia*> media, - int size, - int frame) { - Expects(media->loaded()); - - return std::make_shared<Lottie::Icon>(Lottie::IconDescriptor{ - .path = media->owner()->filepath(true), - .json = media->bytes(), - .sizeOverride = QSize(size, size), - .frame = frame, - }); -} - -} // namespace - -Button::Button( - Fn<void(QRect)> update, - ButtonParameters parameters, - Fn<void(bool expanded)> toggleExpanded, - Fn<void()> hide) -: _update(std::move(update)) -, _toggleExpanded(std::move(toggleExpanded)) -, _finalScale(ScaleForState(_state)) -, _collapsed(QPoint(), CountOuterSize()) -, _finalHeight(_collapsed.height()) -, _expandTimer([=] { _toggleExpanded(true); }) -, _hideTimer(hide) { - applyParameters(parameters, nullptr); -} - -Button::~Button() = default; - -void Button::expandWithoutCustom() { - applyState(State::Inside, _update); -} - -bool Button::isHidden() const { - return (_state == State::Hidden) && !_opacityAnimation.animating(); -} - -QRect Button::geometry() const { - return _geometry; -} - -int Button::expandedHeight() const { - return _expandedHeight; -} - -int Button::scroll() const { - return _scroll; -} - -int Button::scrollMax() const { - return _expandedInnerHeight - _expandedHeight; -} - -float64 Button::expandAnimationOpacity(float64 expandRatio) const { - return (_collapseType == CollapseType::Fade) - ? expandRatio - : 1.; -} - -int Button::expandAnimationScroll(float64 expandRatio) const { - return (_collapseType == CollapseType::Scroll && expandRatio < 1.) - ? std::clamp(int(base::SafeRound(expandRatio * _scroll)), 0, _scroll) - : _scroll; -} - -bool Button::expandUp() const { - return (_expandDirection == ExpandDirection::Up); -} - -bool Button::consumeWheelEvent(not_null<QWheelEvent*> e) { - const auto scrollMax = (_expandedInnerHeight - _expandedHeight); - if (_state != State::Inside - || scrollMax <= 0 - || !_geometry.contains(LocalPosition(e))) { - return false; - } - const auto delta = e->angleDelta(); - const auto horizontal = std::abs(delta.x()) > std::abs(delta.y()); - if (horizontal) { - return false; - } - const auto between = st::reactionCornerSkip; - const auto oneHeight = (st::reactionCornerSize.height() + between); - const auto max = oneHeight * kMaxReactionsScrollAtOnce; - const auto shift = std::clamp( - delta.y() * (expandUp() ? 1 : -1), - -max, - max); - _scroll = std::clamp(_scroll + shift, 0, scrollMax); - _update(_geometry); - e->accept(); - return true; -} - -void Button::applyParameters(ButtonParameters parameters) { - applyParameters(std::move(parameters), _update); -} - -void Button::applyParameters( - ButtonParameters parameters, - Fn<void(QRect)> update) { - const auto shift = parameters.center - _collapsed.center(); - _collapsed = _collapsed.translated(shift); - updateGeometry(update); - const auto inner = _geometry.marginsRemoved(st::reactionCornerShadow); - const auto active = inner.marginsAdded( - st::reactionCornerActiveAreaPadding - ).contains(parameters.pointer); - const auto inside = inner.contains(parameters.pointer) - || (active && (_state == State::Inside)); - if (_state != State::Inside && !_heightAnimation.animating()) { - updateExpandDirection(parameters); - } - const auto delayInside = inside && (_state != State::Inside); - if (!delayInside) { - _expandTimer.cancel(); - _lastGlobalPosition = std::nullopt; - } else { - const auto globalPositionChanged = _lastGlobalPosition - && (*_lastGlobalPosition != parameters.globalPointer); - if (globalPositionChanged || _state == State::Hidden) { - _expandTimer.callOnce(kButtonExpandDelay); - } - _lastGlobalPosition = parameters.globalPointer; - } - const auto wasInside = (_state == State::Inside); - const auto state = (inside && !delayInside) - ? State::Inside - : active - ? State::Active - : State::Shown; - applyState(state, update); - if (parameters.outside && _state == State::Shown) { - _hideTimer.callOnce(wasInside - ? kButtonExpandedHideDelay - : kButtonHideDelay); - } else { - _hideTimer.cancel(); - } -} - -void Button::updateExpandDirection(const ButtonParameters ¶meters) { - const auto maxAddedHeight = (parameters.reactionsCount - 1) - * (st::reactionCornerSize.height() + st::reactionCornerSkip) - + (parameters.reactionsCount > 1 ? 2 * st::reactionExpandedSkip : 0); - _expandedInnerHeight = _collapsed.height() + maxAddedHeight; - const auto addedHeight = std::min( - maxAddedHeight, - st::reactionCornerAddedHeightMax); - _expandedHeight = _collapsed.height() + addedHeight; - _scroll = std::clamp(_scroll, 0, scrollMax()); - if (parameters.reactionsCount < 2) { - return; - } - const auto up = (_collapsed.y() - addedHeight >= parameters.visibleTop) - || (_collapsed.y() + _collapsed.height() + addedHeight - > parameters.visibleBottom); - _expandDirection = up ? ExpandDirection::Up : ExpandDirection::Down; -} - -void Button::updateGeometry(Fn<void(QRect)> update) { - const auto added = int(base::SafeRound( - _heightAnimation.value(_finalHeight) - )) - _collapsed.height(); - if (!added && _state != State::Inside) { - _scroll = 0; - } - const auto geometry = _collapsed.marginsAdded({ - 0, - (_expandDirection == ExpandDirection::Up) ? added : 0, - 0, - (_expandDirection == ExpandDirection::Down) ? added : 0, - }); - if (_geometry != geometry) { - if (update) { - update(_geometry); - } - _geometry = geometry; - if (update) { - update(_geometry); - } - } -} - -void Button::applyState(State state) { - applyState(state, _update); -} - -void Button::applyState(State state, Fn<void(QRect)> update) { - if (state == State::Hidden) { - _expandTimer.cancel(); - _hideTimer.cancel(); - } - const auto finalHeight = (state == State::Hidden) - ? _heightAnimation.value(_finalHeight) - : (state == State::Inside) - ? _expandedHeight - : _collapsed.height(); - if (_finalHeight != finalHeight) { - if (state == State::Hidden) { - _heightAnimation.stop(); - } else { - if (!_heightAnimation.animating()) { - _collapseType = (_scroll < st::reactionCollapseFadeThreshold) - ? CollapseType::Scroll - : CollapseType::Fade; - } - _heightAnimation.start( - [=] { updateGeometry(_update); }, - _finalHeight, - finalHeight, - (state == State::Inside - ? kExpandDuration - : kCollapseDuration), - anim::easeOutCirc); - } - _finalHeight = finalHeight; - } - updateGeometry(update); - if (_state == state) { - return; - } - const auto duration = (state == State::Hidden || _state == State::Hidden) - ? kToggleDuration - : kActivateDuration; - const auto finalScale = ScaleForState(state); - _opacityAnimation.start( - [=] { _update(_geometry); }, - OpacityForScale(ScaleForState(_state)), - OpacityForScale(ScaleForState(state)), - duration, - anim::sineInOut); - if (state != State::Hidden && _finalScale != finalScale) { - _scaleAnimation.start( - [=] { _update(_geometry); }, - _finalScale, - finalScale, - duration, - anim::sineInOut); - _finalScale = finalScale; - } - _state = state; - _toggleExpanded(false); -} - -float64 Button::ScaleForState(State state) { - switch (state) { - case State::Hidden: return 1. / 3; - case State::Shown: return 2. / 3; - case State::Active: - case State::Inside: return 1.; - } - Unexpected("State in ReactionButton::ScaleForState."); -} - -float64 Button::OpacityForScale(float64 scale) { - return std::min( - ((scale - ScaleForState(State::Hidden)) - / (ScaleForState(State::Shown) - ScaleForState(State::Hidden))), - 1.); -} - -float64 Button::currentScale() const { - return _scaleAnimation.value(_finalScale); -} - -float64 Button::currentOpacity() const { - return _opacityAnimation.value(OpacityForScale(ScaleForState(_state))); -} - -Manager::Manager( - QWidget *wheelEventsTarget, - rpl::producer<int> uniqueLimitValue, - Fn<void(QRect)> buttonUpdate, - IconFactory iconFactory) -: _iconFactory(std::move(iconFactory)) -, _outer(CountOuterSize()) -, _inner(QRect({}, st::reactionCornerSize)) -, _cachedRound( - st::reactionCornerSize, - st::reactionCornerShadow, - _inner.width()) -, _uniqueLimit(std::move(uniqueLimitValue)) -, _buttonShowTimer([=] { showButtonDelayed(); }) -, _buttonUpdate(std::move(buttonUpdate)) { - _inner.translate(QRect({}, _outer).center() - _inner.center()); - - _emojiParts = _cachedRound.PrepareFramesCache(_outer); - _expandedBuffer = _cachedRound.PrepareImage(QSize( - _outer.width(), - _outer.height() + st::reactionCornerAddedHeightMax)); - if (wheelEventsTarget) { - stealWheelEvents(wheelEventsTarget); - } - - _uniqueLimit.changes( - ) | rpl::start_with_next([=] { - applyListFilters(); - }, _lifetime); - - _createChooseCallback = [=](ReactionId id) { - return [=] { - if (auto chosen = lookupChosen(id)) { - _chosen.fire(std::move(chosen)); - } - }; - }; -} - -ChosenReaction Manager::lookupChosen(const ReactionId &id) const { - auto result = ChosenReaction{ - .context = _buttonContext, - .id = id, - }; - const auto button = _button.get(); - const auto i = ranges::find(_icons, id, &ReactionIcons::id); - if (i == end(_icons) || !button) { - return result; - } - const auto &icon = *i; - if (const auto &appear = icon->appear; appear && appear->animating()) { - result.icon = CreateIcon( - icon->appearAnimation->activeMediaView().get(), - appear->width(), - appear->frameIndex()); - } else if (const auto &select = icon->select) { - result.icon = CreateIcon( - icon->selectAnimation->activeMediaView().get(), - select->width(), - select->frameIndex()); - } - const auto index = (i - begin(_icons)); - const auto between = st::reactionCornerSkip; - const auto oneHeight = (st::reactionCornerSize.height() + between); - const auto expanded = (_icons.size() > 1); - const auto skip = (expanded ? st::reactionExpandedSkip : 0); - const auto scroll = button->scroll(); - const auto local = skip + index * oneHeight - scroll; - const auto geometry = button->geometry(); - const auto top = button->expandUp() - ? (geometry.height() - local - _outer.height()) - : local; - const auto rect = QRect(geometry.topLeft() + QPoint(0, top), _outer); - const auto imageSize = int(base::SafeRound( - st::reactionCornerImage * kHoverScale)); - result.geometry = QRect( - rect.x() + (rect.width() - imageSize) / 2, - rect.y() + (rect.height() - imageSize) / 2, - imageSize, - imageSize); - return result; -} - -bool Manager::applyUniqueLimit() const { - const auto limit = _uniqueLimit.current(); - return _buttonContext - && (limit > 0) - && (_buttonAlreadyNotMineCount >= limit); -} - -void Manager::applyListFilters() { - const auto limited = applyUniqueLimit(); - auto icons = std::vector<not_null<ReactionIcons*>>(); - icons.reserve(_list.size()); - auto showPremiumLock = (ReactionIcons*)nullptr; - auto favoriteIndex = -1; - for (auto &icon : _list) { - const auto &id = icon.id; - const auto add = limited - ? _buttonAlreadyList.contains(id) - : id.emoji().isEmpty() - ? _filter.customAllowed - : (!_filter.allowed || _filter.allowed->contains(id.emoji())); - if (add) { - if (icon.premium - && !_allowSendingPremium - && !_buttonAlreadyList.contains(id)) { - if (_premiumPossible) { - showPremiumLock = &icon; - } else { - clearStateForHidden(icon); - } - } else { - icon.premiumLock = false; - if (id == _favorite) { - favoriteIndex = int(icons.size()); - } - icons.push_back(&icon); - } - } else { - clearStateForHidden(icon); - } - } - if (showPremiumLock) { - showPremiumLock->premiumLock = true; - icons.push_back(showPremiumLock); - } - if (favoriteIndex > 0) { - const auto first = begin(icons); - std::rotate(first, first + favoriteIndex, first + favoriteIndex + 1); - } - if (!limited && _filter.customAllowed && icons.size() > 1) { - icons.erase(begin(icons) + 1, end(icons)); - } - if (_icons == icons) { - return; - } - const auto selected = _selectedIcon; - setSelectedIcon(-1); - _icons = std::move(icons); - setSelectedIcon((selected < _icons.size()) ? selected : -1); - resolveMainReactionIcon(); -} - -void Manager::stealWheelEvents(not_null<QWidget*> target) { - base::install_event_filter(target, [=](not_null<QEvent*> e) { - if (e->type() != QEvent::Wheel - || !consumeWheelEvent(static_cast<QWheelEvent*>(e.get()))) { - return base::EventFilterResult::Continue; - } - Ui::SendSynteticMouseEvent(target, QEvent::MouseMove, Qt::NoButton); - return base::EventFilterResult::Cancel; - }); -} - -Manager::~Manager() = default; - -void Manager::updateButton(ButtonParameters parameters) { - if (parameters.cursorLeft) { - if (_menu) { - return; - } else if (_externalSelectorShown) { - setSelectedIcon(-1); - return; - } - } - const auto contextChanged = (_buttonContext != parameters.context); - if (contextChanged) { - setSelectedIcon(-1); - if (_button) { - _button->applyState(ButtonState::Hidden); - _buttonHiding.push_back(std::move(_button)); - } - _buttonShowTimer.cancel(); - _scheduledParameters = std::nullopt; - } - _buttonContext = parameters.context; - parameters.reactionsCount = _icons.size(); - if (!_buttonContext || !parameters.reactionsCount) { - return; - } else if (_button) { - _button->applyParameters(parameters); - if (_button->geometry().height() == _outer.height()) { - clearAppearAnimations(); - } - return; - } else if (parameters.outside) { - _buttonShowTimer.cancel(); - _scheduledParameters = std::nullopt; - return; - } - const auto globalPositionChanged = _scheduledParameters - && (_scheduledParameters->globalPointer != parameters.globalPointer); - const auto positionChanged = _scheduledParameters - && (_scheduledParameters->pointer != parameters.pointer); - _scheduledParameters = parameters; - if ((_buttonShowTimer.isActive() && positionChanged) - || globalPositionChanged) { - _buttonShowTimer.callOnce(kButtonShowDelay); - } -} - -void Manager::toggleExpanded(bool expanded) { - if (!_button || !_buttonContext) { - } else if (!expanded || (_filter.customAllowed && !applyUniqueLimit())) { - _expandSelectorRequests.fire({ - .context = _buttonContext, - .button = _button->geometry().marginsRemoved( - st::reactionCornerShadow), - .expanded = expanded, - }); - } else { - _button->expandWithoutCustom(); - } -} - -void Manager::setExternalSelectorShown(rpl::producer<bool> shown) { - std::move(shown) | rpl::start_with_next([=](bool shown) { - _externalSelectorShown = shown; - }, _lifetime); -} - -void Manager::showButtonDelayed() { - clearAppearAnimations(); - _button = std::make_unique<Button>( - _buttonUpdate, - *_scheduledParameters, - [=](bool expanded) { toggleExpanded(expanded); }, - [=]{ updateButton({}); }); -} - -void Manager::applyList( - const std::vector<Data::Reaction> &list, - const ReactionId &favorite, - bool premiumPossible) { - const auto possibleChanged = (_premiumPossible != premiumPossible); - _premiumPossible = premiumPossible; - const auto proj = [](const auto &obj) { - return std::tie( - obj.id, - obj.appearAnimation, - obj.selectAnimation, - obj.premium); - }; - const auto favoriteChanged = (_favorite != favorite); - if (favoriteChanged) { - _favorite = favorite; - } - if (ranges::equal(_list, list, ranges::equal_to(), proj, proj)) { - if (favoriteChanged || possibleChanged) { - applyListFilters(); - } - return; - } - const auto selected = _selectedIcon; - setSelectedIcon(-1); - _icons.clear(); - _list.clear(); - for (const auto &reaction : list) { - _list.push_back({ - .id = reaction.id, - .appearAnimation = reaction.appearAnimation, - .selectAnimation = reaction.selectAnimation, - .premium = reaction.premium, - }); - } - applyListFilters(); - setSelectedIcon((selected < _icons.size()) ? selected : -1); -} - -void Manager::updateFilter(Data::ReactionsFilter filter) { - if (_filter == filter) { - return; - } - _filter = std::move(filter); - applyListFilters(); -} - -void Manager::updateAllowSendingPremium(bool allow) { - if (_allowSendingPremium == allow) { - return; - } - _allowSendingPremium = allow; - applyListFilters(); -} - -const Data::ReactionsFilter &Manager::filter() const { - return _filter; -} - -void Manager::updateUniqueLimit(not_null<HistoryItem*> item) { - if (item->fullId() != _buttonContext) { - return; - } - const auto &all = item->reactions(); - const auto my = item->chosenReaction(); - auto list = base::flat_set<Data::ReactionId>(); - list.reserve(all.size()); - auto myIsUnique = false; - for (const auto &[id, count] : all) { - list.emplace(id); - if (count == 1 && id == my) { - myIsUnique = true; - } - } - const auto notMineCount = int(list.size()) - (myIsUnique ? 1 : 0); - - auto changed = false; - if (_buttonAlreadyList != list) { - _buttonAlreadyList = std::move(list); - changed = true; - } - if (_buttonAlreadyNotMineCount != notMineCount) { - _buttonAlreadyNotMineCount = notMineCount; - changed = true; - } - if (changed) { - applyListFilters(); - } -} - -void Manager::resolveMainReactionIcon() { - if (_icons.empty()) { - _mainReactionMedia = nullptr; - _mainReactionLifetime.destroy(); - return; - } - const auto main = _icons.front()->selectAnimation; - _icons.front()->appearAnimated = true; - if (_mainReactionMedia && _mainReactionMedia->owner() == main) { - if (!_mainReactionLifetime) { - loadIcons(); - } - return; - } - _mainReactionMedia = main->createMediaView(); - _mainReactionMedia->checkStickerLarge(); - if (_mainReactionMedia->loaded()) { - _mainReactionLifetime.destroy(); - setMainReactionIcon(); - } else if (!_mainReactionLifetime) { - main->session().downloaderTaskFinished( - ) | rpl::filter([=] { - return _mainReactionMedia->loaded(); - }) | rpl::take(1) | rpl::start_with_next([=] { - setMainReactionIcon(); - }, _mainReactionLifetime); - } -} - -void Manager::setMainReactionIcon() { - _mainReactionLifetime.destroy(); - ranges::fill(_validEmoji, false); - loadIcons(); - const auto i = _loadCache.find(_mainReactionMedia->owner()); - if (i != end(_loadCache) && i->second.icon) { - const auto &icon = i->second.icon; - if (!icon->frameIndex() && icon->width() == MainReactionSize()) { - _mainReactionImage = i->second.icon->frame(); - return; - } - } - _mainReactionImage = QImage(); - _mainReactionIcon = DefaultIconFactory( - _mainReactionMedia.get(), - MainReactionSize()); -} - -QMargins Manager::innerMargins() const { - return { - _inner.x(), - _inner.y(), - _outer.width() - _inner.x() - _inner.width(), - _outer.height() - _inner.y() - _inner.height(), - }; -} - -QRect Manager::buttonInner() const { - return buttonInner(_button.get()); -} - -QRect Manager::buttonInner(not_null<Button*> button) const { - return button->geometry().marginsRemoved(innerMargins()); -} - -bool Manager::checkIconLoaded(ReactionDocument &entry) const { - if (!entry.media) { - return true; - } else if (!entry.media->loaded()) { - return false; - } - const auto size = (entry.media == _mainReactionMedia) - ? MainReactionSize() - : CornerImageSize(1.); - entry.icon = _iconFactory(entry.media.get(), size); - entry.media = nullptr; - return true; -} - -void Manager::updateCurrentButton() const { - if (const auto button = _button.get()) { - _buttonUpdate(button->geometry()); - } -} - -void Manager::loadIcons() { - const auto load = [&](not_null<DocumentData*> document) { - if (const auto i = _loadCache.find(document); i != end(_loadCache)) { - return i->second.icon; - } - auto &entry = _loadCache.emplace(document).first->second; - entry.media = document->createMediaView(); - entry.media->checkStickerLarge(); - if (!checkIconLoaded(entry) && !_loadCacheLifetime) { - document->session().downloaderTaskFinished( - ) | rpl::start_with_next([=] { - checkIcons(); - }, _loadCacheLifetime); - } - return entry.icon; - }; - auto all = true; - for (const auto &icon : _icons) { - if (!icon->appear) { - icon->appear = load(icon->appearAnimation); - } - if (!icon->select) { - icon->select = load(icon->selectAnimation); - } - if (!icon->appear || !icon->select) { - all = false; - } - } - if (all && !_icons.empty()) { - auto &data = _icons.front()->appearAnimation->owner().reactions(); - for (const auto &icon : _icons) { - data.preloadAnimationsFor(icon->id); - } - } -} - -void Manager::checkIcons() { - auto all = true; - for (auto &[document, entry] : _loadCache) { - if (!checkIconLoaded(entry)) { - all = false; - } - } - if (all) { - _loadCacheLifetime.destroy(); - loadIcons(); - } -} - -void Manager::removeStaleButtons() { - _buttonHiding.erase( - ranges::remove_if(_buttonHiding, &Button::isHidden), - end(_buttonHiding)); -} - -void Manager::paint(Painter &p, const PaintContext &context) { - removeStaleButtons(); - for (const auto &button : _buttonHiding) { - paintButton(p, context, button.get()); - } - if (const auto current = _button.get()) { - paintButton(p, context, current); - } - - for (const auto &[id, effect] : _collectedEffects) { - const auto offset = effect.effectOffset; - p.translate(offset); - _activeEffectAreas[id] = effect.effectPaint(p).translated(offset); - p.translate(-offset); - } - _collectedEffects.clear(); -} - -ClickHandlerPtr Manager::computeButtonLink(QPoint position) const { - if (_icons.empty()) { - setSelectedIcon(-1); - return nullptr; - } - const auto inner = buttonInner(); - const auto top = _button->expandUp() - ? (inner.y() + inner.height() - position.y()) - : (position.y() - inner.y()); - const auto shifted = top + _button->scroll(); - const auto between = st::reactionCornerSkip; - const auto oneHeight = (st::reactionCornerSize.height() + between); - const auto index = std::clamp( - int(base::SafeRound(shifted + between / 2.)) / oneHeight, - 0, - int(_icons.size() - 1)); - auto &result = _icons[index]->link; - if (!result) { - result = resolveButtonLink(*_icons[index]); - } - setSelectedIcon(index); - return result; -} - -void Manager::setSelectedIcon(int index) const { - const auto setSelected = [&](int index, bool selected) { - if (index < 0 || index >= _icons.size()) { - return; - } - const auto &icon = _icons[index]; - if (icon->selected == selected) { - return; - } - icon->selected = selected; - icon->selectedScale.start( - [=] { updateCurrentButton(); }, - selected ? 1. : kHoverScale, - selected ? kHoverScale : 1., - kHoverScaleDuration, - anim::sineInOut); - if (selected) { - const auto skipAnimation = icon->selectAnimated - || !icon->appearAnimated - || (icon->select && icon->select->animating()) - || (icon->appear && icon->appear->animating()); - const auto select = skipAnimation ? nullptr : icon->select.get(); - if (select && !icon->selectAnimated) { - icon->selectAnimated = true; - select->animate( - crl::guard(this, [=] { updateCurrentButton(); }), - 0, - select->framesCount() - 1); - } - } - }; - if (_selectedIcon != index) { - setSelected(_selectedIcon, false); - _selectedIcon = index; - } - setSelected(index, true); -} - -ClickHandlerPtr Manager::resolveButtonLink( - const ReactionIcons &reaction) const { - const auto id = reaction.id; - const auto i = _reactionsLinks.find(id); - if (i != end(_reactionsLinks)) { - return i->second; - } - auto handler = std::make_shared<LambdaClickHandler>( - crl::guard(this, _createChooseCallback(id))); - handler->setProperty( - kSendReactionEmojiProperty, - QVariant::fromValue(id)); - return _reactionsLinks.emplace(id, std::move(handler)).first->second; -} - -TextState Manager::buttonTextState(QPoint position) const { - if (overCurrentButton(position)) { - auto result = TextState(nullptr, computeButtonLink(position)); - result.itemId = _buttonContext; - return result; - } else { - setSelectedIcon(-1); - } - return {}; -} - -bool Manager::overCurrentButton(QPoint position) const { - if (!_button) { - return false; - } - return _button && buttonInner().contains(position); -} - -void Manager::remove(FullMsgId context) { - _activeEffectAreas.remove(context); - if (_buttonContext == context) { - _buttonContext = {}; - _button = nullptr; - } -} - -bool Manager::consumeWheelEvent(not_null<QWheelEvent*> e) { - return _button && _button->consumeWheelEvent(e); -} - -void Manager::paintButton( - Painter &p, - const PaintContext &context, - not_null<Button*> button) { - const auto geometry = button->geometry(); - if (!context.clip.intersects(geometry)) { - return; - } - const auto scale = button->currentScale(); - const auto scaleMin = Button::ScaleForState(ButtonState::Hidden); - const auto scaleMax = Button::ScaleForState(ButtonState::Active); - const auto progress = (scale - scaleMin) / (scaleMax - scaleMin); - const auto frame = int(base::SafeRound(progress * (kFramesCount - 1))); - const auto useScale = scaleMin - + (frame / float64(kFramesCount - 1)) * (scaleMax - scaleMin); - paintButton(p, context, button, frame, useScale); -} - -void Manager::paintButton( - Painter &p, - const PaintContext &context, - not_null<Button*> button, - int frameIndex, - float64 scale) { - const auto opacity = button->currentOpacity(); - if (opacity == 0.) { - return; - } - - const auto geometry = button->geometry(); - const auto position = geometry.topLeft(); - const auto size = geometry.size(); - const auto expanded = (size.height() - _outer.height()); - if (opacity != 1.) { - p.setOpacity(opacity); - } - auto layeredPainter = std::optional<Painter>(); - if (expanded) { - _expandedBuffer.fill(Qt::transparent); - } - const auto q = expanded ? &layeredPainter.emplace(&_expandedBuffer) : &p; - const auto shadow = context.st->shadowFg()->c; - const auto background = context.st->windowBg()->c; - _cachedRound.setShadowColor(shadow); - _cachedRound.setBackgroundColor(background); - if (expanded) { - q->fillRect(QRect(QPoint(), size), context.st->windowBg()); - } else { - const auto frame = _cachedRound.validateFrame(frameIndex, scale); - p.drawImage(position, *frame.image, frame.rect); - } - - const auto current = (button == _button.get()); - const auto expandRatio = expanded - ? std::clamp( - float64(expanded) / (button->expandedHeight() - _outer.height()), - 0., - 1.) - : 0.; - const auto expandedSkip = int(base::SafeRound( - expandRatio * st::reactionExpandedSkip)); - const auto mainEmojiPosition = !expanded - ? position - : button->expandUp() - ? QPoint(0, expanded - expandedSkip) - : QPoint(0, expandedSkip); - const auto source = validateEmoji(frameIndex, scale); - if (expanded - || (current && !onlyMainEmojiVisible()) - || (_icons.size() == 1 && _icons.front()->premiumLock)) { - const auto origin = expanded ? QPoint() : position; - const auto scroll = button->expandAnimationScroll(expandRatio); - const auto opacity = button->expandAnimationOpacity(expandRatio); - if (opacity != 1.) { - q->setOpacity(opacity); - } - paintAllEmoji(*q, button, scroll, scale, origin, mainEmojiPosition); - if (opacity != 1.) { - q->setOpacity(1.); - } - if (current && expanded) { - _showingAll = true; - } - if (expanded) { - paintInnerGradients(*q, background, button, scroll, expandRatio); - } - if (opacity != 1.) { - const auto appearShift = st::reactionMainAppearShift * opacity; - const auto appearPosition = !expanded - ? position - : button->expandUp() - ? QPoint(0, expanded - appearShift) - : QPoint(0, appearShift); - q->setOpacity(1. - opacity); - q->drawImage(appearPosition, _emojiParts, source); - q->setOpacity(1.); - } - } else { - p.drawImage(mainEmojiPosition, _emojiParts, source); - } - if (current && !expanded) { - clearAppearAnimations(); - } - - if (expanded) { - const auto radiusMin = _inner.height() / 2.; - const auto radiusMax = _inner.width() / 2.; - _cachedRound.overlayExpandedBorder( - *q, - size, - expandRatio, - radiusMin + expandRatio * (radiusMax - radiusMin), - scale); - layeredPainter.reset(); - p.drawImage( - geometry, - _expandedBuffer, - QRect(QPoint(), size * style::DevicePixelRatio())); - } - if (opacity != 1.) { - p.setOpacity(1.); - } -} - -void Manager::paintInnerGradients( - Painter &p, - const QColor &background, - not_null<Button*> button, - int scroll, - float64 expandRatio) { - if (_gradientBackground != background) { - _gradientBackground = background; - _topGradient = _bottomGradient = QImage(); - } - const auto endScroll = button->scrollMax() - scroll; - const auto size = st::reactionGradientSize; - const auto ensureGradient = [&](QImage &gradient, bool top) { - if (!gradient.isNull()) { - return; - } - gradient = Images::GenerateShadow( - size, - top ? 255 : 0, - top ? 0 : 255, - background); - }; - ensureGradient(_topGradient, true); - ensureGradient(_bottomGradient, false); - const auto paintGradient = [&](QImage &gradient, int scrolled, int top) { - if (scrolled <= 0) { - return; - } - const auto opacity = (expandRatio * scrolled) - / st::reactionGradientFadeSize; - p.setOpacity(opacity); - p.drawImage( - QRect(0, top, _outer.width(), size), - gradient, - QRect(QPoint(), gradient.size())); - }; - const auto up = button->expandUp(); - const auto start = st::reactionGradientStart; - paintGradient(_topGradient, up ? endScroll : scroll, start); - const auto bottomStart = button->geometry().height() - start - size; - paintGradient(_bottomGradient, up ? scroll : endScroll, bottomStart); - p.setOpacity(1.); -} - -bool Manager::onlyMainEmojiVisible() const { - if (_icons.empty()) { - return true; - } - const auto &icon = _icons.front(); - if (icon->selected - || icon->selectedScale.animating() - || (icon->select && icon->select->animating())) { - return false; - } - icon->selectAnimated = false; - return true; -} - -void Manager::clearAppearAnimations() { - if (!_showingAll) { - return; - } - _showingAll = false; - auto main = true; - for (auto &icon : _icons) { - if (!main) { - if (icon->selected) { - setSelectedIcon(-1); - } - icon->selectedScale.stop(); - if (const auto select = icon->select.get()) { - select->jumpTo(0, nullptr); - } - icon->selectAnimated = false; - } - if (icon->appearAnimated != main) { - if (const auto appear = icon->appear.get()) { - appear->jumpTo(0, nullptr); - } - icon->appearAnimated = main; - } - main = false; - } -} - -void Manager::paintAllEmoji( - Painter &p, - not_null<Button*> button, - int scroll, - float64 scale, - QPoint position, - QPoint mainEmojiPosition) { - const auto current = (button == _button.get()); - - const auto clip = QRect( - position, - button->geometry().size()).marginsRemoved(innerMargins()); - const auto skip = st::reactionAppearStartSkip; - const auto animationRect = clip.marginsRemoved({ 0, skip, 0, skip }); - - PainterHighQualityEnabler hq(p); - const auto between = st::reactionCornerSkip; - const auto oneHeight = st::reactionCornerSize.height() + between; - const auto finalSize = CornerImageSize(1.); - const auto hoveredSize = int(base::SafeRound(finalSize * kHoverScale)); - const auto basicTargetForScale = [&](int size, float64 scale) { - const auto remove = size * (1. - scale) / 2.; - return QRectF(QRect( - _inner.x() + (_inner.width() - size) / 2, - _inner.y() + (_inner.height() - size) / 2, - size, - size - )).marginsRemoved({ remove, remove, remove, remove }); - }; - const auto basicTarget = basicTargetForScale(finalSize, scale); - const auto countTarget = [&](const ReactionIcons &icon) { - const auto selectScale = icon.selectedScale.value( - icon.selected ? kHoverScale : 1.); - if (selectScale == 1.) { - return basicTarget; - } - const auto finalScale = scale * selectScale; - return (finalScale <= 1.) - ? basicTargetForScale(finalSize, finalScale) - : basicTargetForScale(hoveredSize, finalScale / kHoverScale); - }; - const auto expandUp = button->expandUp(); - const auto shift = QPoint(0, oneHeight * (expandUp ? -1 : 1)); - auto emojiPosition = mainEmojiPosition - + QPoint(0, scroll * (expandUp ? 1 : -1)); - const auto update = crl::guard(this, [=] { - updateCurrentButton(); - }); - for (const auto &icon : _icons) { - const auto target = countTarget(*icon).translated(emojiPosition); - emojiPosition += shift; - - const auto paintFrame = [&](not_null<Lottie::Icon*> 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 (current) { - clearStateForHidden(*icon); - } - } else if (icon->premiumLock) { - paintPremiumIcon(p, emojiPosition - shift, target); - } else { - const auto appear = icon->appear.get(); - if (current - && 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 (current) { - clearStateForSelectFinished(*icon); - } - } -} - -void Manager::paintPremiumIcon( - QPainter &p, - QPoint position, - QRectF target) const { - const auto finalSize = CornerImageSize(1.); - const auto to = QRect( - _inner.x() + (_inner.width() - finalSize) / 2, - _inner.y() + (_inner.height() - finalSize) / 2, - finalSize, - finalSize).translated(position); - const auto scale = target.width() / to.width(); - if (scale != 1.) { - p.save(); - p.translate(target.center()); - p.scale(scale, scale); - p.translate(-target.center()); - } - auto hq = PainterHighQualityEnabler(p); - st::reactionPremiumLocked.paintInCenter(p, to); - if (scale != 1.) { - p.restore(); - } -} - -void Manager::clearStateForHidden(ReactionIcons &icon) { - if (const auto appear = icon.appear.get()) { - appear->jumpTo(0, nullptr); - } - if (icon.selected) { - setSelectedIcon(-1); - } - icon.appearAnimated = false; - icon.selectAnimated = false; - if (const auto select = icon.select.get()) { - select->jumpTo(0, nullptr); - } - icon.selectedScale.stop(); -} - -void Manager::clearStateForSelectFinished(ReactionIcons &icon) { - if (icon.selectAnimated - && !icon.select->animating() - && !icon.selected) { - icon.selectAnimated = false; - } -} - -QRect Manager::validateEmoji(int frameIndex, float64 scale) { - const auto result = _cachedRound.FrameCacheRect( - frameIndex, - kEmojiCacheIndex, - _outer); - if (_validEmoji[frameIndex]) { - return result; - } - - auto p = QPainter(&_emojiParts); - const auto ratio = style::DevicePixelRatio(); - const auto position = result.topLeft() / ratio; - p.setCompositionMode(QPainter::CompositionMode_Source); - p.fillRect(QRect(position, result.size() / ratio), Qt::transparent); - if (_mainReactionImage.isNull() - && _mainReactionIcon) { - _mainReactionImage = base::take(_mainReactionIcon)->frame(); - } - if (!_mainReactionImage.isNull()) { - const auto size = CornerImageSize(scale); - const auto inner = _inner.translated(position); - const auto target = QRect( - inner.x() + (inner.width() - size) / 2, - inner.y() + (inner.height() - size) / 2, - size, - size); - - p.drawImage(target, _mainReactionImage.scaled( - target.size() * ratio, - Qt::IgnoreAspectRatio, - Qt::SmoothTransformation)); - } - - _validEmoji[frameIndex] = true; - return result; -} - -std::optional<QRect> Manager::lookupEffectArea(FullMsgId itemId) const { - const auto i = _activeEffectAreas.find(itemId); - return (i != end(_activeEffectAreas)) - ? i->second - : std::optional<QRect>(); -} - -void Manager::startEffectsCollection() { - _collectedEffects.clear(); - _currentReactionInfo = {}; -} - -auto Manager::currentReactionPaintInfo() --> not_null<Ui::ReactionPaintInfo*> { - return &_currentReactionInfo; -} - -void Manager::recordCurrentReactionEffect(FullMsgId itemId, QPoint origin) { - if (_currentReactionInfo.effectPaint) { - _currentReactionInfo.effectOffset += origin - + _currentReactionInfo.position; - _collectedEffects[itemId] = base::take(_currentReactionInfo); - } else if (!_collectedEffects.empty()) { - _collectedEffects.remove(itemId); - } -} - -bool Manager::showContextMenu( - QWidget *parent, - QContextMenuEvent *e, - const ReactionId &favorite) { - if (_icons.empty() || _selectedIcon < 0) { - return false; - } - const auto lookupSelectedId = [&] { - const auto i = ranges::find(_icons, true, &ReactionIcons::selected); - return (i != end(_icons)) ? (*i)->id : ReactionId(); - }; - if (!favorite.empty() && lookupSelectedId() == favorite) { - return true; - } - _menu = base::make_unique_q<Ui::PopupMenu>( - parent, - st::popupMenuWithIcons); - const auto callback = [=] { - if (const auto id = lookupSelectedId(); !id.empty()) { - _faveRequests.fire_copy(id); - } - }; - _menu->addAction( - tr::lng_context_set_as_quick(tr::now), - callback, - &st::menuIconFave); - _menu->popup(e->globalPos()); - return true; -} - -auto Manager::faveRequests() const -> rpl::producer<ReactionId> { - return _faveRequests.events(); -} - -void SetupManagerList( - not_null<Manager*> manager, - not_null<Main::Session*> session, - rpl::producer<Data::ReactionsFilter> filter) { - const auto reactions = &session->data().reactions(); - rpl::single(rpl::empty) | rpl::then( - reactions->updates() - ) | rpl::start_with_next([=] { - manager->applyList( - reactions->list(Data::Reactions::Type::Active), - reactions->favorite(), - session->premiumPossible()); - }, manager->lifetime()); - - std::move( - filter - ) | rpl::start_with_next([=](Data::ReactionsFilter &&list) { - manager->updateFilter(std::move(list)); - }, manager->lifetime()); - - manager->faveRequests( - ) | rpl::start_with_next([=](const Data::ReactionId &id) { - reactions->setFavorite(id); - manager->updateButton({}); - }, manager->lifetime()); - - Data::AmPremiumValue( - session - ) | rpl::start_with_next([=](bool premium) { - manager->updateAllowSendingPremium(premium); - }, manager->lifetime()); -} - -IconFactory CachedIconFactory::createMethod() { - return [=](not_null<Data::DocumentMedia*> media, int size) { - const auto owned = media->owner()->createMediaView(); - const auto i = _cache.find(owned); - return (i != end(_cache)) - ? i->second - : _cache.emplace( - owned, - DefaultIconFactory(media, size)).first->second; - }; -} - -std::shared_ptr<Lottie::Icon> DefaultIconFactory( - not_null<Data::DocumentMedia*> media, - int size) { - return CreateIcon(media, size, 0); -} - -} // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/reactions/_history_view_reactions_button.h b/Telegram/SourceFiles/history/view/reactions/_history_view_reactions_button.h deleted file mode 100644 index f12ce3768..000000000 --- a/Telegram/SourceFiles/history/view/reactions/_history_view_reactions_button.h +++ /dev/null @@ -1,373 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include "ui/effects/animations.h" -#include "ui/effects/round_area_with_shadow.h" -#include "ui/widgets/scroll_area.h" -#include "data/data_message_reaction_id.h" -#include "ui/chat/chat_style.h" - -namespace Ui { -struct ChatPaintContext; -class PopupMenu; -} // namespace Ui - -namespace Data { -struct Reaction; -class DocumentMedia; -} // namespace Data - -namespace HistoryView { -using PaintContext = Ui::ChatPaintContext; -struct TextState; -} // namespace HistoryView - -namespace Main { -class Session; -} // namespace Main - -namespace Lottie { -class Icon; -} // namespace Lottie - -namespace HistoryView::Reactions { - -enum class ExpandDirection { - Up, - Down, -}; - -struct ButtonParameters { - [[nodiscard]] ButtonParameters translated(QPoint delta) const { - auto result = *this; - result.center += delta; - result.pointer += delta; - return result; - } - - FullMsgId context; - QPoint center; - QPoint pointer; - QPoint globalPointer; - int reactionsCount = 1; - int visibleTop = 0; - int visibleBottom = 0; - bool outside = false; - bool cursorLeft = false; -}; - -enum class ButtonState { - Hidden, - Shown, - Active, - Inside, -}; - -class Button final { -public: - Button( - Fn<void(QRect)> update, - ButtonParameters parameters, - Fn<void(bool)> toggleExpanded, - Fn<void()> hide); - ~Button(); - - void applyParameters(ButtonParameters parameters); - void expandWithoutCustom(); - - using State = ButtonState; - void applyState(State state); - - [[nodiscard]] bool expandUp() const; - [[nodiscard]] bool isHidden() const; - [[nodiscard]] QRect geometry() const; - [[nodiscard]] int expandedHeight() const; - [[nodiscard]] int scroll() const; - [[nodiscard]] int scrollMax() const; - [[nodiscard]] float64 currentScale() const; - [[nodiscard]] float64 currentOpacity() const; - [[nodiscard]] float64 expandAnimationOpacity(float64 expandRatio) const; - [[nodiscard]] int expandAnimationScroll(float64 expandRatio) const; - [[nodiscard]] bool consumeWheelEvent(not_null<QWheelEvent*> e); - - [[nodiscard]] static float64 ScaleForState(State state); - [[nodiscard]] static float64 OpacityForScale(float64 scale); - -private: - enum class CollapseType { - Scroll, - Fade, - }; - - void updateGeometry(Fn<void(QRect)> update); - void applyState(State satte, Fn<void(QRect)> update); - void applyParameters( - ButtonParameters parameters, - Fn<void(QRect)> update); - void updateExpandDirection(const ButtonParameters ¶meters); - - const Fn<void(QRect)> _update; - const Fn<void(bool)> _toggleExpanded; - - State _state = State::Hidden; - float64 _finalScale = 0.; - Ui::Animations::Simple _scaleAnimation; - Ui::Animations::Simple _opacityAnimation; - Ui::Animations::Simple _heightAnimation; - - QRect _collapsed; - QRect _geometry; - int _expandedInnerHeight = 0; - int _expandedHeight = 0; - int _finalHeight = 0; - int _scroll = 0; - ExpandDirection _expandDirection = ExpandDirection::Up; - CollapseType _collapseType = CollapseType::Scroll; - - base::Timer _expandTimer; - base::Timer _hideTimer; - std::optional<QPoint> _lastGlobalPosition; - -}; - -struct ChosenReaction { - FullMsgId context; - Data::ReactionId id; - std::shared_ptr<Lottie::Icon> icon; - QRect geometry; - - explicit operator bool() const { - return context && !id.empty(); - } -}; - -struct ExpandRequest { - FullMsgId context; - QRect button; - bool expanded = false; -}; - -using IconFactory = Fn<std::shared_ptr<Lottie::Icon>( - not_null<Data::DocumentMedia*>, - int)>; - -class Manager final : public base::has_weak_ptr { -public: - Manager( - QWidget *wheelEventsTarget, - rpl::producer<int> uniqueLimitValue, - Fn<void(QRect)> buttonUpdate, - IconFactory iconFactory); - ~Manager(); - - using ReactionId = ::Data::ReactionId; - - void applyList( - const std::vector<Data::Reaction> &list, - const ReactionId &favorite, - bool premiumPossible); - void updateFilter(Data::ReactionsFilter filter); - void updateAllowSendingPremium(bool allow); - [[nodiscard]] const Data::ReactionsFilter &filter() const; - void updateUniqueLimit(not_null<HistoryItem*> item); - - void updateButton(ButtonParameters parameters); - void paint(Painter &p, const PaintContext &context); - [[nodiscard]] TextState buttonTextState(QPoint position) const; - void remove(FullMsgId context); - - [[nodiscard]] bool consumeWheelEvent(not_null<QWheelEvent*> e); - - [[nodiscard]] rpl::producer<ChosenReaction> chosen() const { - return _chosen.events(); - } - [[nodiscard]] auto expandSelectorRequests() const - -> rpl::producer<ExpandRequest> { - return _expandSelectorRequests.events(); - } - void setExternalSelectorShown(rpl::producer<bool> shown); - - [[nodiscard]] std::optional<QRect> lookupEffectArea( - FullMsgId itemId) const; - void startEffectsCollection(); - [[nodiscard]] auto currentReactionPaintInfo() - -> not_null<Ui::ReactionPaintInfo*>; - void recordCurrentReactionEffect(FullMsgId itemId, QPoint origin); - - bool showContextMenu( - QWidget *parent, - QContextMenuEvent *e, - const ReactionId &favorite); - [[nodiscard]] rpl::producer<ReactionId> faveRequests() const; - - [[nodiscard]] rpl::lifetime &lifetime() { - return _lifetime; - } - -private: - struct ReactionDocument { - std::shared_ptr<Data::DocumentMedia> media; - std::shared_ptr<Lottie::Icon> icon; - }; - struct ReactionIcons { - ReactionId id; - not_null<DocumentData*> appearAnimation; - not_null<DocumentData*> selectAnimation; - std::shared_ptr<Lottie::Icon> appear; - std::shared_ptr<Lottie::Icon> select; - mutable ClickHandlerPtr link; - mutable Ui::Animations::Simple selectedScale; - bool appearAnimated = false; - bool premium = false; - bool premiumLock = false; - mutable bool selected = false; - mutable bool selectAnimated = false; - }; - static constexpr auto kFramesCount = Ui::RoundAreaWithShadow::kFramesCount; - - void applyListFilters(); - void showButtonDelayed(); - void stealWheelEvents(not_null<QWidget*> target); - - [[nodiscard]] ChosenReaction lookupChosen(const ReactionId &id) const; - [[nodiscard]] bool overCurrentButton(QPoint position) const; - [[nodiscard]] bool applyUniqueLimit() const; - void toggleExpanded(bool expanded); - - void removeStaleButtons(); - void paintButton( - Painter &p, - const PaintContext &context, - not_null<Button*> button); - void paintButton( - Painter &p, - const PaintContext &context, - not_null<Button*> button, - int frame, - float64 scale); - void paintAllEmoji( - Painter &p, - not_null<Button*> button, - int scroll, - float64 scale, - QPoint position, - QPoint mainEmojiPosition); - void paintPremiumIcon(QPainter &p, QPoint position, QRectF target) const; - void paintInnerGradients( - Painter &p, - const QColor &background, - not_null<Button*> button, - int scroll, - float64 expandRatio); - - void resolveMainReactionIcon(); - void setMainReactionIcon(); - void clearAppearAnimations(); - [[nodiscard]] QRect cacheRect(int frameIndex, int columnIndex) const; - QRect validateEmoji(int frameIndex, float64 scale); - - void setSelectedIcon(int index) const; - void clearStateForHidden(ReactionIcons &icon); - void clearStateForSelectFinished(ReactionIcons &icon); - - [[nodiscard]] QMargins innerMargins() const; - [[nodiscard]] QRect buttonInner() const; - [[nodiscard]] QRect buttonInner(not_null<Button*> button) const; - - [[nodiscard]] ClickHandlerPtr computeButtonLink(QPoint position) const; - [[nodiscard]] ClickHandlerPtr resolveButtonLink( - const ReactionIcons &reaction) const; - - void updateCurrentButton() const; - [[nodiscard]] bool onlyMainEmojiVisible() const; - [[nodiscard]] bool checkIconLoaded(ReactionDocument &entry) const; - void loadIcons(); - void checkIcons(); - - const IconFactory _iconFactory; - rpl::event_stream<ChosenReaction> _chosen; - rpl::event_stream<ExpandRequest> _expandSelectorRequests; - std::vector<ReactionIcons> _list; - ReactionId _favorite; - Data::ReactionsFilter _filter; - QSize _outer; - QRect _inner; - Ui::RoundAreaWithShadow _cachedRound; - QImage _emojiParts; - QImage _expandedBuffer; - QColor _gradientBackground; - QImage _topGradient; - QImage _bottomGradient; - std::array<bool, kFramesCount> _validEmoji = { { false } }; - QColor _gradient; - - std::shared_ptr<Data::DocumentMedia> _mainReactionMedia; - std::shared_ptr<Lottie::Icon> _mainReactionIcon; - QImage _mainReactionImage; - rpl::lifetime _mainReactionLifetime; - - rpl::variable<int> _uniqueLimit = 0; - base::flat_map<not_null<DocumentData*>, ReactionDocument> _loadCache; - std::vector<not_null<ReactionIcons*>> _icons; - std::optional<ReactionIcons> _premiumIcon; - rpl::lifetime _loadCacheLifetime; - bool _showingAll = false; - bool _allowSendingPremium = false; - bool _premiumPossible = false; - mutable int _selectedIcon = -1; - - std::optional<ButtonParameters> _scheduledParameters; - base::Timer _buttonShowTimer; - const Fn<void(QRect)> _buttonUpdate; - std::unique_ptr<Button> _button; - std::vector<std::unique_ptr<Button>> _buttonHiding; - FullMsgId _buttonContext; - base::flat_set<ReactionId> _buttonAlreadyList; - int _buttonAlreadyNotMineCount = 0; - mutable base::flat_map<ReactionId, ClickHandlerPtr> _reactionsLinks; - Fn<Fn<void()>(ReactionId)> _createChooseCallback; - - base::flat_map<FullMsgId, QRect> _activeEffectAreas; - - Ui::ReactionPaintInfo _currentReactionInfo; - base::flat_map<FullMsgId, Ui::ReactionPaintInfo> _collectedEffects; - - base::unique_qptr<Ui::PopupMenu> _menu; - rpl::event_stream<ReactionId> _faveRequests; - bool _externalSelectorShown = false; - - rpl::lifetime _lifetime; - -}; - -class CachedIconFactory final { -public: - CachedIconFactory() = default; - CachedIconFactory(const CachedIconFactory &other) = delete; - CachedIconFactory &operator=(const CachedIconFactory &other) = delete; - - [[nodiscard]] IconFactory createMethod(); - -private: - base::flat_map< - std::shared_ptr<Data::DocumentMedia>, - std::shared_ptr<Lottie::Icon>> _cache; - -}; - -void SetupManagerList( - not_null<Manager*> manager, - not_null<Main::Session*> session, - rpl::producer<Data::ReactionsFilter> filter); - -[[nodiscard]] std::shared_ptr<Lottie::Icon> DefaultIconFactory( - not_null<Data::DocumentMedia*> media, - int size); - -} // namespace HistoryView