Allow to open / remove sets from emoji panel.

This commit is contained in:
John Preston 2022-07-11 16:39:35 +03:00
parent a7e295ae64
commit 54d683171d
7 changed files with 419 additions and 214 deletions

View file

@ -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<OverButton>(!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<OverEmoji>(&_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<OverEmoji>(&_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<OverEmoji>(&_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<OverSet>(&pressed)) {
Assert(set->section >= kEmojiSectionCount
&& set->section < kEmojiSectionCount + _custom.size());
displaySet(_custom[set->section - kEmojiSectionCount].id);
} else if (auto button = std::get_if<OverButton>(&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<StickerSetBox>(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<DocumentData*> 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<OverEmoji>(&_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<OverEmoji>(&_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<OverEmoji>(&_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<OverEmoji>(&_selected)) {
rtlupdate(emojiRect(sticker->section, sticker->index));
} else if (const auto button = std::get_if<OverButton>(&_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<OverButton>(&_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<OverButton>(&_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<Ui::RippleAnimation> 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<Ui::RippleAnimation>(
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();

View file

@ -16,6 +16,10 @@ template <typename ...Tags>
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<CustomOne> list;
std::unique_ptr<Ui::RippleAnimation> ripple;
bool painted = false;
};
struct RepaintSet {
base::flat_set<uint64> 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 <typename Callback>
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<DocumentData*> 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<Ui::RippleAnimation> 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<EmojiColorPicker> _picker;

View file

@ -1195,10 +1195,6 @@ StickersListWidget::StickersListWidget(
}, lifetime());
}
Main::Session &StickersListWidget::session() const {
return controller()->session();
}
rpl::producer<TabbedSelector::FileChosen> 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<TabbedSelector::Action> {
return _choosingUpdated.events();
@ -2554,7 +2546,6 @@ QPoint StickersListWidget::buttonRippleTopLeft(int section) const {
void StickersListWidget::showStickerSetBox(not_null<DocumentData*> document) {
if (document->sticker() && document->sticker()->set) {
_displayingSet = true;
checkHideWithBox(StickerSetBox::Show(controller(), document));
}
}
@ -3364,10 +3355,6 @@ std::vector<StickerIcon> 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<StickersBox>(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<StickerSetBox>(controller(), it->second->identifier()),
Ui::LayerOption::KeepOther).data());
}
}
void StickersListWidget::checkHideWithBox(QPointer<Ui::BoxContent> 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<void()> &&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<void()> &&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<void()> &&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<Ui::BoxContent> MakeConfirmRemoveSetBox(
not_null<Main::Session*> 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<void()> &&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<void()> &&close) {
close();
},
.confirmText = tr::lng_stickers_remove_pack_confirm(),
});
}
} // namespace ChatHelpers

View file

@ -55,11 +55,8 @@ public:
not_null<Window::SessionController*> controller,
bool masks = false);
Main::Session &session() const;
rpl::producer<TabbedSelector::FileChosen> chosen() const;
rpl::producer<> scrollUpdated() const;
rpl::producer<> checkForHide() const;
rpl::producer<TabbedSelector::Action> choosingUpdated() const;
void refreshRecent() override;
@ -76,7 +73,6 @@ public:
void refreshStickers();
std::vector<StickerIcon> fillIcons();
bool preventAutoHide();
uint64 currentSet(int yOffset) const;
@ -205,7 +201,6 @@ private:
void setSection(Section section);
void displaySet(uint64 setId);
void checkHideWithBox(QPointer<Ui::BoxContent> box);
void installSet(uint64 setId);
void removeMegagroupSet(bool locally);
void removeSet(uint64 setId);
@ -367,7 +362,6 @@ private:
base::Timer _updateSetsTimer;
base::flat_set<uint64> _repaintSetsIds;
bool _displayingSet = false;
uint64 _removingSetId = 0;
Footer *_footer = nullptr;
@ -405,9 +399,12 @@ private:
rpl::event_stream<TabbedSelector::FileChosen> _chosen;
rpl::event_stream<> _scrollUpdated;
rpl::event_stream<> _checkForHide;
rpl::event_stream<TabbedSelector::Action> _choosingUpdated;
};
[[nodiscard]] object_ptr<Ui::BoxContent> MakeConfirmRemoveSetBox(
not_null<Main::Session*> session,
uint64 setId);
} // namespace ChatHelpers

View file

@ -280,8 +280,9 @@ void TabbedPanel::opacityAnimationCallback() {
}
void TabbedPanel::hideByTimerOrLeave() {
if (isHidden() || preventAutoHide()) return;
if (isHidden() || preventAutoHide()) {
return;
}
hideAnimated();
}

View file

@ -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<int> 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<Ui::BoxContent> box) {
if (!box) {
return;
}
_preventHideWithBox = true;
connect(box, &QObject::destroyed, this, [=] {
_preventHideWithBox = false;
_checkForHide.fire({});
});
}
void TabbedSelector::Inner::visibleTopBottomUpdated(
int visibleTop,
int visibleBottom) {

View file

@ -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<Window::SessionController*> controller);
not_null<Window::SessionController*> controller() const {
[[nodiscard]] not_null<Window::SessionController*> 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<Ui::BoxContent> box);
private:
not_null<Window::SessionController*> _controller;
@ -318,6 +329,9 @@ private:
rpl::event_stream<int> _scrollToRequests;
rpl::event_stream<bool> _disableScrollRequests;
rpl::event_stream<> _checkForHide;
bool _preventHideWithBox = false;
};