From 54d683171d4556482e3600f6ae8685265c4f2f97 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 11 Jul 2022 16:39:35 +0300 Subject: [PATCH] Allow to open / remove sets from emoji panel. --- .../chat_helpers/emoji_list_widget.cpp | 315 +++++++++++++----- .../chat_helpers/emoji_list_widget.h | 64 +++- .../chat_helpers/stickers_list_widget.cpp | 194 +++++------ .../chat_helpers/stickers_list_widget.h | 11 +- .../SourceFiles/chat_helpers/tabbed_panel.cpp | 5 +- .../chat_helpers/tabbed_selector.cpp | 24 +- .../chat_helpers/tabbed_selector.h | 20 +- 7 files changed, 419 insertions(+), 214 deletions(-) diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp index 79d92b377..e0faaff5c 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp @@ -11,15 +11,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" #include "ui/widgets/shadow.h" #include "ui/text/custom_emoji_instance.h" +#include "ui/effects/ripple_animation.h" #include "ui/emoji_config.h" #include "ui/ui_utility.h" #include "ui/cached_round_corners.h" +#include "boxes/sticker_set_box.h" #include "lang/lang_keys.h" #include "layout/layout_position.h" #include "data/data_session.h" #include "data/data_document.h" #include "data/stickers/data_stickers.h" #include "data/stickers/data_custom_emoji.h" +#include "chat_helpers/stickers_list_widget.h" #include "emoji_suggestions_data.h" #include "emoji_suggestions_helper.h" #include "main/main_session.h" @@ -708,6 +711,9 @@ void EmojiListWidget::paintEvent(QPaintEvent *e) { const auto paused = controller()->isGifPausedAtLeastFor( Window::GifPauseReason::SavedGifs); const auto now = crl::now(); + auto selectedButton = std::get_if(!v::is_null(_pressed) + ? &_pressed + : &_selected); enumerateSections([&](const SectionInfo &info) { if (r.top() >= info.rowsBottom) { return true; @@ -717,14 +723,32 @@ void EmojiListWidget::paintEvent(QPaintEvent *e) { if (info.section > 0 && r.top() < info.rowsTop) { p.setFont(st::emojiPanHeaderFont); p.setPen(st::emojiPanHeaderFg); - const auto text = (info.section < kEmojiSectionCount) + auto titleText = (info.section < kEmojiSectionCount) ? ChatHelpers::EmojiCategoryTitle(info.section)(tr::now) : _custom[info.section - kEmojiSectionCount].title; - p.drawTextLeft( - st::emojiPanHeaderLeft - st::roundRadiusSmall, - info.top + st::emojiPanHeaderTop, - width(), - text); + auto titleWidth = st::stickersTrendingHeaderFont->width(titleText); + auto widthForTitle = emojiRight() - (st::emojiPanHeaderLeft - st::roundRadiusSmall); + if (hasRemoveButton(info.section)) { + auto &custom = _custom[info.section - kEmojiSectionCount]; + auto remove = removeButtonRect(info.section); + auto selected = selectedButton ? (selectedButton->section == info.section) : false; + if (custom.ripple) { + custom.ripple->paint(p, remove.x() + st::stickerPanRemoveSet.rippleAreaPosition.x(), remove.y() + st::stickerPanRemoveSet.rippleAreaPosition.y(), width()); + if (custom.ripple->empty()) { + custom.ripple.reset(); + } + } + (selected ? st::stickerPanRemoveSet.iconOver : st::stickerPanRemoveSet.icon).paint(p, remove.topLeft() + st::stickerPanRemoveSet.iconPosition, width()); + + widthForTitle -= remove.width(); + } + if (titleWidth > widthForTitle) { + titleText = st::stickersTrendingHeaderFont->elided(titleText, widthForTitle); + titleWidth = st::stickersTrendingHeaderFont->width(titleText); + } + p.setFont(st::emojiPanHeaderFont); + p.setPen(st::emojiPanHeaderFg); + p.drawTextLeft(st::emojiPanHeaderLeft - st::roundRadiusSmall, info.top + st::emojiPanHeaderTop, width(), titleText, titleWidth); } if (r.top() + r.height() > info.rowsTop) { ensureLoaded(info.section); @@ -735,12 +759,13 @@ void EmojiListWidget::paintEvent(QPaintEvent *e) { auto index = i * _columnCount + j; if (index >= info.count) break; - const auto selectedIndex = Layout::PositionToIndex( - info.section, - index); - auto selected = (selectedIndex == _selected) + const auto state = OverEmoji{ + .section = info.section, + .index = index, + }; + const auto selected = (state == _selected) || (!_picker->isHidden() - && selectedIndex == _pickerSel); + && state == _pickerSelected); auto w = QPoint(_rowsLeft + j * _singleSize.width(), info.rowsTop + i * _singleSize.height()); if (selected) { @@ -794,9 +819,9 @@ void EmojiListWidget::drawCustom( } bool EmojiListWidget::checkPickerHide() { - if (!_picker->isHidden() && _pickerSel >= 0) { + if (!_picker->isHidden() && !v::is_null(_pickerSelected)) { _picker->hideAnimated(); - _pickerSel = -1; + _pickerSelected = v::null; updateSelected(); return true; } @@ -809,17 +834,15 @@ void EmojiListWidget::mousePressEvent(QMouseEvent *e) { if (checkPickerHide() || e->button() != Qt::LeftButton) { return; } - _pressedSel = _selected; - - if (_selected >= 0) { - const auto &[section, sel] = Layout::IndexToPosition(_selected); - if (section < kEmojiSectionCount - && sel < _emoji[section].size() - && _emoji[section][sel]->hasVariants()) { - _pickerSel = _selected; + setPressed(_selected); + if (const auto over = std::get_if(&_selected)) { + if (over->section < kEmojiSectionCount + && over->index < _emoji[over->section].size() + && _emoji[over->section][over->index]->hasVariants()) { + _pickerSelected = _selected; setCursor(style::cur_default); const auto &variants = Core::App().settings().emojiVariants(); - if (!variants.contains(_emoji[section][sel]->nonColoredId())) { + if (!variants.contains(_emoji[over->section][over->index]->nonColoredId())) { showPicker(); } else { _showPickerTimer.callOnce(500); @@ -829,22 +852,22 @@ void EmojiListWidget::mousePressEvent(QMouseEvent *e) { } void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) { - int32 pressed = _pressedSel; - _pressedSel = -1; - + auto pressed = _pressed; + setPressed(v::null); _lastMousePos = e->globalPos(); if (!_picker->isHidden()) { if (_picker->rect().contains(_picker->mapFromGlobal(_lastMousePos))) { return _picker->handleMouseRelease(QCursor::pos()); - } else if (_pickerSel >= 0) { - const auto &[section, sel] = Layout::IndexToPosition(_pickerSel); + } else if (const auto over = std::get_if(&_pickerSelected)) { + const auto section = over->section; + const auto index = over->index; if (section < kEmojiSectionCount - && sel < _emoji[section].size() - && _emoji[section][sel]->hasVariants()) { + && index < _emoji[section].size() + && _emoji[section][index]->hasVariants()) { const auto &variants = Core::App().settings().emojiVariants(); - if (variants.contains(_emoji[section][sel]->nonColoredId())) { + if (variants.contains(_emoji[section][index]->nonColoredId())) { _picker->hideAnimated(); - _pickerSel = -1; + _pickerSelected = v::null; } } } @@ -853,25 +876,54 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) { if (_showPickerTimer.isActive()) { _showPickerTimer.cancel(); - _pickerSel = -1; + _pickerSelected = v::null; _picker->hide(); } - if (_selected < 0 || _selected != pressed) { + if (v::is_null(_selected) || _selected != pressed) { return; } - const auto &[section, sel] = Layout::IndexToPosition(_selected); - if (section < kEmojiSectionCount && sel < _emoji[section].size()) { - const auto emoji = _emoji[section][sel]; - if (emoji->hasVariants() && !_picker->isHidden()) { - return; + if (const auto over = std::get_if(&_selected)) { + const auto section = over->section; + const auto index = over->index; + if (section < kEmojiSectionCount && index < _emoji[section].size()) { + const auto emoji = _emoji[section][index]; + if (emoji->hasVariants() && !_picker->isHidden()) { + return; + } + selectEmoji(emoji); + } else if (section >= kEmojiSectionCount + && index < _custom[section - kEmojiSectionCount].list.size()) { + auto &set = _custom[section - kEmojiSectionCount]; + selectCustom(set.list[index].document); } - selectEmoji(emoji); - } else if (section >= kEmojiSectionCount - && sel < _custom[section - kEmojiSectionCount].list.size()) { - auto &set = _custom[section - kEmojiSectionCount]; - selectCustom(set.list[sel].document); + } else if (const auto set = std::get_if(&pressed)) { + Assert(set->section >= kEmojiSectionCount + && set->section < kEmojiSectionCount + _custom.size()); + displaySet(_custom[set->section - kEmojiSectionCount].id); + } else if (auto button = std::get_if(&pressed)) { + Assert(button->section >= kEmojiSectionCount + && button->section < kEmojiSectionCount + _custom.size()); + removeSet(_custom[button->section - kEmojiSectionCount].id); + } +} + +void EmojiListWidget::displaySet(uint64 setId) { + const auto &sets = session().data().stickers().sets(); + auto it = sets.find(setId); + if (it != sets.cend()) { + checkHideWithBox(controller()->show( + Box(controller(), it->second->identifier()), + Ui::LayerOption::KeepOther).data()); + } +} + +void EmojiListWidget::removeSet(uint64 setId) { + if (auto box = MakeConfirmRemoveSetBox(&session(), setId)) { + checkHideWithBox(controller()->show( + std::move(box), + Ui::LayerOption::KeepOther)); } } @@ -885,19 +937,25 @@ void EmojiListWidget::selectCustom(not_null document) { } void EmojiListWidget::showPicker() { - if (_pickerSel < 0) return; + if (v::is_null(_pickerSelected)) { + return; + } - const auto &[section, sel] = Layout::IndexToPosition(_pickerSel); - if (section < kEmojiSectionCount && sel < _emoji[section].size() && _emoji[section][sel]->hasVariants()) { - _picker->showEmoji(_emoji[section][sel]); + const auto over = std::get_if(&_pickerSelected); + const auto section = over ? over->section : -1; + if (section >= 0 + && section < kEmojiSectionCount + && over->index < _emoji[section].size() + && _emoji[section][over->index]->hasVariants()) { + _picker->showEmoji(_emoji[section][over->index]); - auto y = emojiRect(section, sel).y(); + auto y = emojiRect(section, over->index).y(); y -= _picker->height() - st::roundRadiusSmall + getVisibleTop(); if (y < st::emojiPanHeader) { y += _picker->height() - st::roundRadiusSmall + _singleSize.height() - st::roundRadiusSmall; } auto xmax = width() - _picker->width(); - auto coef = float64(sel % _columnCount) / float64(_columnCount - 1); + auto coef = float64(over->index % _columnCount) / float64(_columnCount - 1); if (rtl()) coef = 1. - coef; _picker->move(qRound(xmax * coef), y); @@ -906,7 +964,7 @@ void EmojiListWidget::showPicker() { } void EmojiListWidget::pickerHidden() { - _pickerSel = -1; + _pickerSelected = v::null; update(); disableScroll(false); @@ -914,14 +972,39 @@ void EmojiListWidget::pickerHidden() { updateSelected(); } -QRect EmojiListWidget::emojiRect(int section, int sel) const { +bool EmojiListWidget::hasRemoveButton(int index) const { + if (index < kEmojiSectionCount + || index >= kEmojiSectionCount + _custom.size()) { + return false; + } + return true; +} + +QRect EmojiListWidget::removeButtonRect(int index) const { + auto buttonw = st::stickerPanRemoveSet.width; + auto buttonh = st::stickerPanRemoveSet.height; + auto buttonx = emojiRight() - buttonw; + auto buttony = sectionInfo(index).top + (st::emojiPanHeader - buttonh) / 2; + return QRect(buttonx, buttony, buttonw, buttonh); +} + +int EmojiListWidget::emojiRight() const { + return emojiLeft() + (_columnCount * _singleSize.width()); +} + +int EmojiListWidget::emojiLeft() const { + return _rowsLeft; +} + +QRect EmojiListWidget::emojiRect(int section, int index) const { Expects(_columnCount > 0); - auto info = sectionInfo(section); - auto countTillItem = (sel - (sel % _columnCount)); - auto rowsToSkip = (countTillItem / _columnCount) + ((countTillItem % _columnCount) ? 1 : 0); - auto x = _rowsLeft + ((sel % _columnCount) * _singleSize.width()); - auto y = info.rowsTop + rowsToSkip * _singleSize.height(); + const auto info = sectionInfo(section); + const auto countTillItem = (index - (index % _columnCount)); + const auto rowsToSkip = (countTillItem / _columnCount) + + ((countTillItem % _columnCount) ? 1 : 0); + const auto x = _rowsLeft + ((index % _columnCount) * _singleSize.width()); + const auto y = info.rowsTop + rowsToSkip * _singleSize.height(); return QRect(x, y, _singleSize.width(), _singleSize.height()); } @@ -929,12 +1012,10 @@ void EmojiListWidget::colorChosen(EmojiPtr emoji) { if (emoji->hasVariants()) { Core::App().settings().saveEmojiVariant(emoji); } - if (_pickerSel >= 0) { - const auto &[section, sel] = Layout::IndexToPosition(_pickerSel); - if (section >= 0 && section < kEmojiSectionCount) { - _emoji[section][sel] = emoji; - rtlupdate(emojiRect(section, sel)); - } + const auto over = std::get_if(&_pickerSelected); + if (over && over->section >= 0 && over->section < kEmojiSectionCount) { + _emoji[over->section][over->index] = emoji; + rtlupdate(emojiRect(over->section, over->index)); } selectEmoji(emoji); _picker->hideAnimated(); @@ -966,9 +1047,9 @@ void EmojiListWidget::enterFromChildEvent(QEvent *e, QWidget *child) { } void EmojiListWidget::clearSelection() { + setPressed(v::null); + setSelected(v::null); _lastMousePos = mapToGlobal(QPoint(-10, -10)); - _pressedSel = -1; - setSelected(-1); } Ui::Emoji::Section EmojiListWidget::currentSection(int yOffset) const { @@ -977,9 +1058,13 @@ Ui::Emoji::Section EmojiListWidget::currentSection(int yOffset) const { QString EmojiListWidget::tooltipText() const { const auto &replacements = Ui::Emoji::internal::GetAllReplacements(); - const auto &[section, sel] = Layout::IndexToPosition(_selected); - if (_selected >= 0 && section < kEmojiSectionCount && sel < _emoji[section].size()) { - const auto emoji = _emoji[section][sel]->original(); + const auto over = std::get_if(&_selected); + const auto section = over ? over->section : -1; + const auto index = over ? over->index : -1; + if (section >= 0 + && section < kEmojiSectionCount + && index < _emoji[section].size()) { + const auto emoji = _emoji[section][index]->original(); const auto text = emoji->text(); // find the replacement belonging to the emoji const auto it = ranges::find_if(replacements, [&](const auto &one) { @@ -1007,7 +1092,7 @@ TabbedSelector::InnerFooter *EmojiListWidget::getFooter() const { void EmojiListWidget::processHideFinished() { if (!_picker->isHidden()) { _picker->hideFast(); - _pickerSel = -1; + _pickerSelected = v::null; } clearSelection(); } @@ -1102,48 +1187,59 @@ bool EmojiListWidget::eventHook(QEvent *e) { } void EmojiListWidget::updateSelected() { - if (_pressedSel >= 0 || _pickerSel >= 0) return; + if (!v::is_null(_pressed) || !v::is_null(_pickerSelected)) { + return; + } - auto newSelected = -1; + auto newSelected = OverState{ v::null }; auto p = mapFromGlobal(_lastMousePos); auto info = sectionInfoByOffset(p.y()); - if (p.y() >= info.rowsTop && p.y() < info.rowsBottom) { + auto section = info.section; + if (p.y() >= info.top && p.y() < info.rowsTop) { + if (hasRemoveButton(section) && myrtlrect(removeButtonRect(section)).contains(p.x(), p.y())) { + newSelected = OverButton{ section }; + } else { + newSelected = OverSet{ section }; + } + } else if (p.y() >= info.rowsTop && p.y() < info.rowsBottom) { auto sx = (rtl() ? width() - p.x() : p.x()) - _rowsLeft; if (sx >= 0 && sx < _columnCount * _singleSize.width()) { - newSelected = qFloor((p.y() - info.rowsTop) / _singleSize.height()) * _columnCount + qFloor(sx / _singleSize.width()); - if (newSelected >= info.count) { - newSelected = -1; - } else { - newSelected += Layout::PositionToIndex(info.section, 0); + const auto index = qFloor((p.y() - info.rowsTop) / _singleSize.height()) * _columnCount + qFloor(sx / _singleSize.width()); + if (index < info.count) { + newSelected = OverEmoji{ .section = section, .index = index }; } } } - setSelected(newSelected); } -void EmojiListWidget::setSelected(int newSelected) { +void EmojiListWidget::setSelected(OverState newSelected) { if (_selected == newSelected) { return; } - auto updateSelected = [this]() { - if (_selected < 0) { - return; + setCursor(!v::is_null(newSelected) + ? style::cur_pointer + : style::cur_default); + + const auto updateSelected = [&] { + if (const auto sticker = std::get_if(&_selected)) { + rtlupdate(emojiRect(sticker->section, sticker->index)); + } else if (const auto button = std::get_if(&_selected)) { + rtlupdate(removeButtonRect(button->section)); } - const auto &[section, sel] = Layout::IndexToPosition(_selected); - rtlupdate(emojiRect(section, sel)); }; updateSelected(); _selected = newSelected; updateSelected(); - if (_selected >= 0 && Core::App().settings().suggestEmoji()) { + const auto hasSelection = !v::is_null(_selected); + if (hasSelection && Core::App().settings().suggestEmoji()) { Ui::Tooltip::Show(1000, this); } - setCursor((_selected >= 0) ? style::cur_pointer : style::cur_default); - if (_selected >= 0 && !_picker->isHidden()) { - if (_selected != _pickerSel) { + setCursor(hasSelection ? style::cur_pointer : style::cur_default); + if (hasSelection && !_picker->isHidden()) { + if (_selected != _pickerSelected) { _picker->hideAnimated(); } else { _picker->showAnimated(); @@ -1151,6 +1247,51 @@ void EmojiListWidget::setSelected(int newSelected) { } } +void EmojiListWidget::setPressed(OverState newPressed) { + if (auto button = std::get_if(&_pressed)) { + Assert(button->section >= kEmojiSectionCount + && button->section < kEmojiSectionCount + _custom.size()); + auto &set = _custom[button->section - kEmojiSectionCount]; + if (set.ripple) { + set.ripple->lastStop(); + } + } + _pressed = newPressed; + if (auto button = std::get_if(&_pressed)) { + Assert(button->section >= kEmojiSectionCount + && button->section < kEmojiSectionCount + _custom.size()); + auto &set = _custom[button->section - kEmojiSectionCount]; + if (!set.ripple) { + set.ripple = createButtonRipple(button->section); + } + set.ripple->add(mapFromGlobal(QCursor::pos()) - buttonRippleTopLeft(button->section)); + } +} + +std::unique_ptr EmojiListWidget::createButtonRipple( + int section) { + Expects(section >= kEmojiSectionCount + && section < kEmojiSectionCount + _custom.size()); + + const auto set = section - kEmojiSectionCount; + auto maskSize = QSize( + st::stickerPanRemoveSet.rippleAreaSize, + st::stickerPanRemoveSet.rippleAreaSize); + auto mask = Ui::RippleAnimation::ellipseMask(maskSize); + return std::make_unique( + st::stickerPanRemoveSet.ripple, + std::move(mask), + [this, section] { rtlupdate(removeButtonRect(section)); }); +} + +QPoint EmojiListWidget::buttonRippleTopLeft(int section) const { + Expects(section >= kEmojiSectionCount + && section < kEmojiSectionCount + _custom.size()); + + return myrtlrect(removeButtonRect(section)).topLeft() + + st::stickerPanRemoveSet.rippleAreaPosition; +} + void EmojiListWidget::showEmojiSection(Section section) { clearSelection(); diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h index 856f9f3a7..0d57bbf2e 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h @@ -16,6 +16,10 @@ template struct phrase; } // namespace tr +namespace Ui { +class RippleAnimation; +} // namespace Ui + namespace Ui::Emoji { enum class Section; } // namespace Ui::Emoji @@ -102,12 +106,50 @@ private: uint64 id = 0; QString title; std::vector list; + std::unique_ptr ripple; bool painted = false; }; struct RepaintSet { base::flat_set ids; crl::time when = 0; }; + struct OverEmoji { + int section = 0; + int index = 0; + + inline bool operator==(OverEmoji other) const { + return (section == other.section) + && (index == other.index); + } + inline bool operator!=(OverEmoji other) const { + return !(*this == other); + } + }; + struct OverSet { + int section = 0; + + inline bool operator==(OverSet other) const { + return (section == other.section); + } + inline bool operator!=(OverSet other) const { + return !(*this == other); + } + }; + struct OverButton { + int section = 0; + + inline bool operator==(OverButton other) const { + return (section == other.section); + } + inline bool operator!=(OverButton other) const { + return !(*this == other); + } + }; + using OverState = std::variant< + v::null_t, + OverEmoji, + OverSet, + OverButton>; template bool enumerateSections(Callback callback) const; @@ -124,7 +166,8 @@ private: void ensureLoaded(int section); void updateSelected(); - void setSelected(int newSelected); + void setSelected(OverState newSelected); + void setPressed(OverState newPressed); void selectEmoji(EmojiPtr emoji); void selectCustom(not_null document); @@ -140,7 +183,18 @@ private: bool paused, int set, int index); - [[nodiscard]] QRect emojiRect(int section, int sel) const; + [[nodiscard]] bool hasRemoveButton(int index) const; + [[nodiscard]] QRect removeButtonRect(int index) const; + [[nodiscard]] QRect emojiRect(int section, int index) const; + [[nodiscard]] int emojiRight() const; + [[nodiscard]] int emojiLeft() const; + + void displaySet(uint64 setId); + void removeSet(uint64 setId); + + [[nodiscard]] std::unique_ptr createButtonRipple( + int section); + [[nodiscard]] QPoint buttonRippleTopLeft(int section) const; void repaintLater( uint64 setId, @@ -162,9 +216,9 @@ private: QSize _singleSize; int _esize = 0; - int _selected = -1; - int _pressedSel = -1; - int _pickerSel = -1; + OverState _selected; + OverState _pressed; + OverState _pickerSelected; QPoint _lastMousePos; object_ptr _picker; diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index 89cac065d..349656946 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -1195,10 +1195,6 @@ StickersListWidget::StickersListWidget( }, lifetime()); } -Main::Session &StickersListWidget::session() const { - return controller()->session(); -} - rpl::producer StickersListWidget::chosen() const { return _chosen.events(); } @@ -1207,10 +1203,6 @@ rpl::producer<> StickersListWidget::scrollUpdated() const { return _scrollUpdated.events(); } -rpl::producer<> StickersListWidget::checkForHide() const { - return _checkForHide.events(); -} - auto StickersListWidget::choosingUpdated() const -> rpl::producer { return _choosingUpdated.events(); @@ -2554,7 +2546,6 @@ QPoint StickersListWidget::buttonRippleTopLeft(int section) const { void StickersListWidget::showStickerSetBox(not_null document) { if (document->sticker() && document->sticker()->set) { - _displayingSet = true; checkHideWithBox(StickerSetBox::Show(controller(), document)); } } @@ -3364,10 +3355,6 @@ std::vector StickersListWidget::fillIcons() { return result; } -bool StickersListWidget::preventAutoHide() { - return _removingSetId != 0 || _displayingSet != 0; -} - void StickersListWidget::updateSelected() { if (!v::is_null(_pressed) && !_previewShown) { return; @@ -3598,7 +3585,6 @@ void StickersListWidget::beforeHiding() { void StickersListWidget::displaySet(uint64 setId) { if (setId == Data::Stickers::MegagroupSetId) { if (_megagroupSet->canEditStickers()) { - _displayingSet = true; checkHideWithBox(controller()->show( Box(controller(), _megagroupSet), Ui::LayerOption::KeepOther).data()); @@ -3612,23 +3598,12 @@ void StickersListWidget::displaySet(uint64 setId) { const auto &sets = session().data().stickers().sets(); auto it = sets.find(setId); if (it != sets.cend()) { - _displayingSet = true; checkHideWithBox(controller()->show( Box(controller(), it->second->identifier()), Ui::LayerOption::KeepOther).data()); } } -void StickersListWidget::checkHideWithBox(QPointer box) { - if (!box) { - return; - } - connect(box, &QObject::destroyed, this, [=] { - _displayingSet = false; - _checkForHide.fire({}); - }); -} - void StickersListWidget::installSet(uint64 setId) { const auto &sets = session().data().stickers().sets(); const auto it = sets.find(setId); @@ -3680,8 +3655,7 @@ void StickersListWidget::removeMegagroupSet(bool locally) { refreshStickers(); return; } - _removingSetId = Data::Stickers::MegagroupSetId; - controller()->show(Ui::MakeConfirmBox({ + checkHideWithBox(controller()->show(Ui::MakeConfirmBox({ .text = tr::lng_stickers_remove_group_set(), .confirmed = crl::guard(this, [this, group = _megagroupSet]( Fn &&close) { @@ -3691,92 +3665,19 @@ void StickersListWidget::removeMegagroupSet(bool locally) { session().api().setGroupStickerSet(group, {}); } close(); - _removingSetId = 0; - _checkForHide.fire({}); }), - .cancelled = crl::guard(this, [this] { - _removingSetId = 0; - _checkForHide.fire({}); + .cancelled = crl::guard(this, [this](Fn &&close) { + close(); }), - })); + }))); } void StickersListWidget::removeSet(uint64 setId) { - const auto &sets = session().data().stickers().sets(); - const auto it = sets.find(setId); - if (it == sets.cend()) { - return; + if (auto box = MakeConfirmRemoveSetBox(&session(), setId)) { + checkHideWithBox(controller()->show( + std::move(box), + Ui::LayerOption::KeepOther)); } - const auto set = it->second.get(); - _removingSetId = set->id; - const auto text = tr::lng_stickers_remove_pack( - tr::now, - lt_sticker_pack, - set->title); - controller()->show(Ui::MakeConfirmBox({ - .text = text, - .confirmed = crl::guard(this, [=](Fn &&close) { - close(); - const auto &sets = session().data().stickers().sets(); - const auto it = sets.find(_removingSetId); - if (it != sets.cend()) { - const auto set = it->second.get(); - if (set->id && set->accessHash) { - _api.request(MTPmessages_UninstallStickerSet( - MTP_inputStickerSetID( - MTP_long(set->id), - MTP_long(set->accessHash))) - ).send(); - } else if (!set->shortName.isEmpty()) { - _api.request(MTPmessages_UninstallStickerSet( - MTP_inputStickerSetShortName( - MTP_string(set->shortName))) - ).send(); - } - auto writeRecent = false; - auto &recent = session().data().stickers().getRecentPack(); - for (auto i = recent.begin(); i != recent.cend();) { - if (set->stickers.indexOf(i->first) >= 0) { - i = recent.erase(i); - writeRecent = true; - } else { - ++i; - } - } - set->flags &= ~SetFlag::Installed; - set->installDate = TimeId(0); - // - // Set can be in search results. - // - //if (!(set->flags & SetFlag::Featured) - // && !(set->flags & SetFlag::Special)) { - // sets.erase(it); - //} - const auto removeIndex = defaultSetsOrder().indexOf( - _removingSetId); - if (removeIndex >= 0) { - defaultSetsOrderRef().removeAt(removeIndex); - } - refreshStickers(); - if (set->flags & SetFlag::Masks) { - session().local().writeInstalledMasks(); - } else { - session().local().writeInstalledStickers(); - } - if (writeRecent) { - session().saveSettings(); - } - session().data().stickers().notifyUpdated(); - } - _removingSetId = 0; - _checkForHide.fire({}); - }), - .cancelled = crl::guard(this, [=] { - _removingSetId = 0; - _checkForHide.fire({}); - }), - .confirmText = tr::lng_stickers_remove_pack_confirm(), - }), Ui::LayerOption::KeepOther); } const Data::StickersSetsOrder &StickersListWidget::defaultSetsOrder() const { @@ -3797,4 +3698,83 @@ bool StickersListWidget::mySetsEmpty() const { StickersListWidget::~StickersListWidget() = default; +object_ptr MakeConfirmRemoveSetBox( + not_null session, + uint64 setId) { + const auto &sets = session->data().stickers().sets(); + const auto it = sets.find(setId); + if (it == sets.cend()) { + return nullptr; + } + const auto set = it->second.get(); + const auto text = tr::lng_stickers_remove_pack( + tr::now, + lt_sticker_pack, + set->title); + return Ui::MakeConfirmBox({ + .text = text, + .confirmed = [=](Fn &&close) { + close(); + const auto &sets = session->data().stickers().sets(); + const auto it = sets.find(setId); + if (it != sets.cend()) { + const auto set = it->second.get(); + if (set->id && set->accessHash) { + session->api().request(MTPmessages_UninstallStickerSet( + MTP_inputStickerSetID( + MTP_long(set->id), + MTP_long(set->accessHash))) + ).send(); + } else if (!set->shortName.isEmpty()) { + session->api().request(MTPmessages_UninstallStickerSet( + MTP_inputStickerSetShortName( + MTP_string(set->shortName))) + ).send(); + } + auto writeRecent = false; + auto &recent = session->data().stickers().getRecentPack(); + for (auto i = recent.begin(); i != recent.cend();) { + if (set->stickers.indexOf(i->first) >= 0) { + i = recent.erase(i); + writeRecent = true; + } else { + ++i; + } + } + set->flags &= ~SetFlag::Installed; + set->installDate = TimeId(0); + // + // Set can be in search results. + // + //if (!(set->flags & SetFlag::Featured) + // && !(set->flags & SetFlag::Special)) { + // sets.erase(it); + //} + auto &orderRef = (set->flags & SetFlag::Emoji) + ? session->data().stickers().emojiSetsOrderRef() + : (set->flags & SetFlag::Masks) + ? session->data().stickers().maskSetsOrderRef() + : session->data().stickers().setsOrderRef(); + const auto removeIndex = orderRef.indexOf(setId); + if (removeIndex >= 0) { + orderRef.removeAt(removeIndex); + } + if (set->flags & SetFlag::Masks) { + session->local().writeInstalledMasks(); + } else { + session->local().writeInstalledStickers(); + } + if (writeRecent) { + session->saveSettings(); + } + session->data().stickers().notifyUpdated(); + } + }, + .cancelled = [=](Fn &&close) { + close(); + }, + .confirmText = tr::lng_stickers_remove_pack_confirm(), + }); +} + } // namespace ChatHelpers diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h index 2748cf7ce..a15a9313d 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h @@ -55,11 +55,8 @@ public: not_null controller, bool masks = false); - Main::Session &session() const; - rpl::producer chosen() const; rpl::producer<> scrollUpdated() const; - rpl::producer<> checkForHide() const; rpl::producer choosingUpdated() const; void refreshRecent() override; @@ -76,7 +73,6 @@ public: void refreshStickers(); std::vector fillIcons(); - bool preventAutoHide(); uint64 currentSet(int yOffset) const; @@ -205,7 +201,6 @@ private: void setSection(Section section); void displaySet(uint64 setId); - void checkHideWithBox(QPointer box); void installSet(uint64 setId); void removeMegagroupSet(bool locally); void removeSet(uint64 setId); @@ -367,7 +362,6 @@ private: base::Timer _updateSetsTimer; base::flat_set _repaintSetsIds; - bool _displayingSet = false; uint64 _removingSetId = 0; Footer *_footer = nullptr; @@ -405,9 +399,12 @@ private: rpl::event_stream _chosen; rpl::event_stream<> _scrollUpdated; - rpl::event_stream<> _checkForHide; rpl::event_stream _choosingUpdated; }; +[[nodiscard]] object_ptr MakeConfirmRemoveSetBox( + not_null session, + uint64 setId); + } // namespace ChatHelpers diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_panel.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_panel.cpp index 12f473136..cf3178580 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_panel.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_panel.cpp @@ -280,8 +280,9 @@ void TabbedPanel::opacityAnimationCallback() { } void TabbedPanel::hideByTimerOrLeave() { - if (isHidden() || preventAutoHide()) return; - + if (isHidden() || preventAutoHide()) { + return; + } hideAnimated(); } diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp index a8e1811a7..972105ed3 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp @@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/discrete_sliders.h" #include "ui/widgets/popup_menu.h" #include "ui/widgets/scroll_area.h" +#include "ui/layers/box_content.h" #include "ui/image/image_prepare.h" #include "ui/cached_round_corners.h" #include "window/window_session_controller.h" @@ -509,7 +510,8 @@ rpl::producer<> TabbedSelector::checkForHide() const { auto never = rpl::never<>(); return rpl::merge( hasStickersTab() ? stickers()->checkForHide() : never, - hasMasksTab() ? masks()->checkForHide() : never); + hasMasksTab() ? masks()->checkForHide() : never, + hasEmojiTab() ? emoji()->checkForHide() : never); } rpl::producer<> TabbedSelector::slideFinished() const { @@ -723,8 +725,9 @@ void TabbedSelector::refreshStickers() { } bool TabbedSelector::preventAutoHide() const { - return (hasStickersTab() ? stickers()->preventAutoHide() : false) - || (hasMasksTab() ? masks()->preventAutoHide() : false) + return (hasStickersTab() && stickers()->preventAutoHide()) + || (hasMasksTab() && masks()->preventAutoHide()) + || (hasEmojiTab() && emoji()->preventAutoHide()) || hasMenu(); } @@ -1133,6 +1136,10 @@ TabbedSelector::Inner::Inner( , _controller(controller) { } +Main::Session &TabbedSelector::Inner::session() const { + return controller()->session(); +} + rpl::producer TabbedSelector::Inner::scrollToRequests() const { return _scrollToRequests.events(); } @@ -1149,6 +1156,17 @@ void TabbedSelector::Inner::disableScroll(bool disabled) { _disableScrollRequests.fire_copy(disabled); } +void TabbedSelector::Inner::checkHideWithBox(QPointer box) { + if (!box) { + return; + } + _preventHideWithBox = true; + connect(box, &QObject::destroyed, this, [=] { + _preventHideWithBox = false; + _checkForHide.fire({}); + }); +} + void TabbedSelector::Inner::visibleTopBottomUpdated( int visibleTop, int visibleBottom) { diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h index 2723e33ee..2d181dfd8 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h @@ -29,6 +29,7 @@ class PopupMenu; class ScrollArea; class SettingsSlider; class FlatLabel; +class BoxContent; } // namespace Ui namespace Window { @@ -259,18 +260,26 @@ class TabbedSelector::Inner : public Ui::RpWidget { public: Inner(QWidget *parent, not_null controller); - not_null controller() const { + [[nodiscard]] not_null controller() const { return _controller; } + [[nodiscard]] Main::Session &session() const; - int getVisibleTop() const { + [[nodiscard]] int getVisibleTop() const { return _visibleTop; } - int getVisibleBottom() const { + [[nodiscard]] int getVisibleBottom() const { return _visibleBottom; } void setMinimalHeight(int newWidth, int newMinimalHeight); + [[nodiscard]] rpl::producer<> checkForHide() const { + return _checkForHide.events(); + } + [[nodiscard]] bool preventAutoHide() const { + return _preventHideWithBox; + } + virtual void refreshRecent() = 0; virtual void preloadImages() { } @@ -309,6 +318,8 @@ protected: void scrollTo(int y); void disableScroll(bool disabled); + void checkHideWithBox(QPointer box); + private: not_null _controller; @@ -318,6 +329,9 @@ private: rpl::event_stream _scrollToRequests; rpl::event_stream _disableScrollRequests; + rpl::event_stream<> _checkForHide; + + bool _preventHideWithBox = false; };