Move EmojiPanel bottom controls to Controller.

Open sticker set by click on the name.
Add a delete sticker set button.
This commit is contained in:
John Preston 2017-03-29 18:09:16 +03:00
parent 6d27172b20
commit 0690c4f98c
15 changed files with 1085 additions and 939 deletions

View File

@ -296,7 +296,7 @@ inline QString ComputeId(Args... args) {\n\
if (!writeInitCode()) {
return false;
}
if (!writePacks()) {
if (!writeSections()) {
return false;
}
source_->stream() << "\
@ -391,22 +391,23 @@ void Init() {\n\
return true;
}
bool Generator::writePacks() {
constexpr const char *packNames[] = {
"dbiesPeople",
"dbiesNature",
"dbiesFood",
"dbiesActivity",
"dbiesTravel",
"dbiesObjects",
"dbiesSymbols",
bool Generator::writeSections() {
constexpr const char *sectionNames[] = {
"Section::People",
"Section::Nature",
"Section::Food",
"Section::Activity",
"Section::Travel",
"Section::Objects",
"Section::Symbols",
};
source_->stream() << "\
\n\
int GetPackCount(DBIEmojiSection section) {\n\
switch (section) {\n";
int GetSectionCount(Section section) {\n\
switch (section) {\n\
case Section::Recent: return GetRecent().size();\n";
auto countIndex = 0;
for (auto name : packNames) {
for (auto name : sectionNames) {
if (countIndex >= int(data_.categories.size())) {
logDataError() << "category " << countIndex << " not found.";
return false;
@ -415,21 +416,29 @@ int GetPackCount(DBIEmojiSection section) {\n\
case " << name << ": return " << data_.categories[countIndex++].size() << ";\n";
}
source_->stream() << "\
case dbiesRecent: return GetRecent().size();\n\
}\n\
return 0;\n\
}\n\
\n\
EmojiPack GetPack(DBIEmojiSection section) {\n\
switch (section) {\n";
EmojiPack GetSection(Section section) {\n\
switch (section) {\n\
case Section::Recent: {\n\
auto result = EmojiPack();\n\
result.reserve(GetRecent().size());\n\
for (auto &item : GetRecent()) {\n\
result.push_back(item.first);\n\
}\n\
return result;\n\
} break;\n";
auto index = 0;
for (auto name : packNames) {
for (auto name : sectionNames) {
if (index >= int(data_.categories.size())) {
logDataError() << "category " << index << " not found.";
return false;
}
auto &category = data_.categories[index++];
source_->stream() << "\
\n\
case " << name << ": {\n\
static auto result = EmojiPack();\n\
if (result.isEmpty()) {\n\
@ -441,17 +450,9 @@ EmojiPack GetPack(DBIEmojiSection section) {\n\
source_->stream() << "\
}\n\
return result;\n\
} break;\n\n";
} break;\n";
}
source_->stream() << "\
case dbiesRecent: {\n\
auto result = EmojiPack();\n\
result.reserve(GetRecent().size());\n\
for (auto &item : GetRecent()) {\n\
result.push_back(item.first);\n\
}\n\
return result;\n\
} break;\n\
}\n\
return EmojiPack();\n\
}\n";

View File

@ -44,7 +44,7 @@ private:
bool writeSource();
bool writeInitCode();
bool writePacks();
bool writeSections();
bool writeFindReplace();
bool writeFind();
bool writeFindFromDictionary(const std::map<QString, int, std::greater<QString>> &dictionary, bool skipPostfixes = false);

View File

@ -527,18 +527,6 @@ enum DBIScale {
static const int MatrixRowShift = 40000;
enum DBIEmojiSection {
dbiesRecent = -1,
dbiesPeople = 0,
dbiesNature = 1,
dbiesFood = 2,
dbiesActivity = 3,
dbiesTravel = 4,
dbiesObjects = 5,
dbiesSymbols = 6,
dbiesStickers = 666,
};
enum DBIPlatform {
dbipWindows = 0,
dbipMac = 1,

View File

@ -28,9 +28,22 @@ namespace base {
template <typename... Types>
using variant = mapbox::util::variant<Types...>;
template <typename... Types>
using optional_variant = variant<std::nullptr_t, Types...>;
template <typename T, typename... Types>
inline T *get_if(variant<Types...> *v) {
return (v && v->template is<T>()) ? &v->template get_unchecked<T>() : nullptr;
}
template <typename T, typename... Types>
inline const T *get_if(const variant<Types...> *v) {
return (v && v->template is<T>()) ? &v->template get_unchecked<T>() : nullptr;
}
template <typename... Types>
inline bool is_null_variant(const optional_variant<Types...> &variant) {
return get_if<std::nullptr_t>(&variant) != nullptr;
}
} // namespace base

View File

@ -20,6 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "stickers/emoji_list_widget.h"
#include "ui/widgets/buttons.h"
#include "styles/style_stickers.h"
#include "ui/widgets/shadow.h"
#include "lang.h"
@ -33,6 +34,70 @@ constexpr auto kSaveRecentEmojiTimeout = 3000;
} // namespace
class EmojiListWidget::Footer : public EmojiPanel::InnerFooter {
public:
Footer(gsl::not_null<EmojiListWidget*> parent);
void setCurrentSectionIcon(Section section);
protected:
void processPanelHideFinished() override;
private:
void prepareSection(int &left, int top, int _width, Ui::IconButton *sectionIcon, Section section);
void setActiveSection(Section section);
gsl::not_null<EmojiListWidget*> _pan;
std::array<object_ptr<Ui::IconButton>, kEmojiSectionCount> _sections;
};
EmojiListWidget::Footer::Footer(gsl::not_null<EmojiListWidget*> parent) : InnerFooter(parent)
, _pan(parent)
, _sections {
object_ptr<Ui::IconButton>(this, st::emojiCategoryRecent),
object_ptr<Ui::IconButton>(this, st::emojiCategoryPeople),
object_ptr<Ui::IconButton>(this, st::emojiCategoryNature),
object_ptr<Ui::IconButton>(this, st::emojiCategoryFood),
object_ptr<Ui::IconButton>(this, st::emojiCategoryActivity),
object_ptr<Ui::IconButton>(this, st::emojiCategoryTravel),
object_ptr<Ui::IconButton>(this, st::emojiCategoryObjects),
object_ptr<Ui::IconButton>(this, st::emojiCategorySymbols),
} {
auto left = (st::emojiPanWidth - _sections.size() * st::emojiCategory.width) / 2;
for (auto i = 0; i != _sections.size(); ++i) {
auto &button = _sections[i];
button->moveToLeft(left, 0);
left += button->width();
button->setClickedCallback([this, value = static_cast<Section>(i)] { setActiveSection(value); });
}
setCurrentSectionIcon(Section::Recent);
}
void EmojiListWidget::Footer::processPanelHideFinished() {
setCurrentSectionIcon(Section::Recent);
}
void EmojiListWidget::Footer::setCurrentSectionIcon(Section section) {
std::array<const style::icon *, kEmojiSectionCount> overrides = {
&st::emojiRecentActive,
&st::emojiPeopleActive,
&st::emojiNatureActive,
&st::emojiFoodActive,
&st::emojiActivityActive,
&st::emojiTravelActive,
&st::emojiObjectsActive,
&st::emojiSymbolsActive,
};
for (auto i = 0; i != _sections.size(); ++i) {
_sections[i]->setIconOverride((section == static_cast<Section>(i)) ? overrides[i] : nullptr);
}
}
void EmojiListWidget::Footer::setActiveSection(Ui::Emoji::Section section) {
_pan->showEmojiSection(section);
}
EmojiColorPicker::EmojiColorPicker(QWidget *parent) : TWidget(parent) {
setMouseTracking(true);
@ -237,19 +302,6 @@ void EmojiColorPicker::drawVariant(Painter &p, int variant) {
p.drawPixmapLeft(w.x() + (st::emojiPanSize.width() - (esize / cIntRetinaFactor())) / 2, w.y() + (st::emojiPanSize.height() - (esize / cIntRetinaFactor())) / 2, width(), App::emojiLarge(), QRect(_variants[variant]->x() * esize, _variants[variant]->y() * esize, esize, esize));
}
class EmojiListWidget::Controller : public TWidget {
public:
Controller(gsl::not_null<EmojiListWidget*> parent);
private:
gsl::not_null<EmojiListWidget*> _pan;
};
EmojiListWidget::Controller::Controller(gsl::not_null<EmojiListWidget*> parent) : TWidget(parent)
, _pan(parent) {
}
EmojiListWidget::EmojiListWidget(QWidget *parent) : Inner(parent)
, _picker(this) {
resize(st::emojiPanWidth - st::emojiScroll.width - st::buttonRadius, countHeight());
@ -262,7 +314,7 @@ EmojiListWidget::EmojiListWidget(QWidget *parent) : Inner(parent)
_esize = Ui::Emoji::Size(Ui::Emoji::Index() + 1);
for (auto i = 0; i != kEmojiSectionCount; ++i) {
_counts[i] = Ui::Emoji::GetPackCount(EmojiSectionAtIndex(i));
_counts[i] = Ui::Emoji::GetSectionCount(static_cast<Section>(i));
}
_showPickerTimer.setSingleShot(true);
@ -271,8 +323,18 @@ EmojiListWidget::EmojiListWidget(QWidget *parent) : Inner(parent)
connect(_picker, SIGNAL(hidden()), this, SLOT(onPickerHidden()));
}
object_ptr<TWidget> EmojiListWidget::createController() {
return object_ptr<Controller>(this);
void EmojiListWidget::setVisibleTopBottom(int visibleTop, int visibleBottom) {
Inner::setVisibleTopBottom(visibleTop, visibleBottom);
if (_footer) {
_footer->setCurrentSectionIcon(currentSection(visibleTop));
}
}
object_ptr<EmojiPanel::InnerFooter> EmojiListWidget::createFooter() {
Expects(_footer == nullptr);
auto result = object_ptr<Footer>(this);
_footer = result;
return std::move(result);
}
template <typename Callback>
@ -280,7 +342,7 @@ bool EmojiListWidget::enumerateSections(Callback callback) const {
auto info = SectionInfo();
for (auto i = 0; i != kEmojiSectionCount; ++i) {
info.section = i;
info.count = Ui::Emoji::GetPackCount(EmojiSectionAtIndex(i));
info.count = Ui::Emoji::GetSectionCount(static_cast<Section>(i));
info.rowsCount = (info.count / kEmojiPanelPerRow) + ((info.count % kEmojiPanelPerRow) ? 1 : 0);
info.rowsTop = info.top + (i == 0 ? st::emojiPanPadding : st::emojiPanHeader);
info.rowsBottom = info.rowsTop + info.rowsCount * st::emojiPanSize.height();
@ -318,15 +380,16 @@ EmojiListWidget::SectionInfo EmojiListWidget::sectionInfoByOffset(int yOffset) c
}
int EmojiListWidget::countHeight() {
return sectionInfo(kEmojiSectionCount - 1).top + st::emojiPanPadding;
return sectionInfo(kEmojiSectionCount - 1).rowsBottom + st::emojiPanPadding;
}
void EmojiListWidget::ensureLoaded(int section) {
Expects(section >= 0 && section < kEmojiSectionCount);
if (!_emoji[section].isEmpty()) {
return;
}
_emoji[section] = Ui::Emoji::GetPack(EmojiSectionAtIndex(section));
if (EmojiSectionAtIndex(section) == dbiesRecent) {
_emoji[section] = Ui::Emoji::GetSection(static_cast<Section>(section));
if (static_cast<Section>(section) == Section::Recent) {
return;
}
for (auto &emoji : _emoji[section]) {
@ -477,7 +540,7 @@ void EmojiListWidget::selectEmoji(EmojiPtr emoji) {
if (i->first == emoji) {
++i->second;
if (i->second > 0x8000) {
for (RecentEmojiPack::iterator j = recent.begin(); j != e; ++j) {
for (auto j = recent.begin(); j != e; ++j) {
if (j->second > 1) {
j->second /= 2;
} else {
@ -596,11 +659,15 @@ void EmojiListWidget::clearSelection() {
setSelected(-1);
}
DBIEmojiSection EmojiListWidget::currentSection(int yOffset) const {
return EmojiSectionAtIndex(sectionInfoByOffset(yOffset).section);
Ui::Emoji::Section EmojiListWidget::currentSection(int yOffset) const {
return static_cast<Section>(sectionInfoByOffset(yOffset).section);
}
void EmojiListWidget::hideFinish(bool completely) {
EmojiPanel::InnerFooter *EmojiListWidget::getFooter() const {
return _footer;
}
void EmojiListWidget::processHideFinished() {
if (!_picker->isHidden()) {
_picker->hideFast();
_pickerSel = -1;
@ -610,8 +677,8 @@ void EmojiListWidget::hideFinish(bool completely) {
void EmojiListWidget::refreshRecent() {
clearSelection();
_counts[0] = Ui::Emoji::GetPackCount(dbiesRecent);
_emoji[0] = Ui::Emoji::GetPack(dbiesRecent);
_counts[0] = Ui::Emoji::GetSectionCount(Section::Recent);
_emoji[0] = Ui::Emoji::GetSection(Section::Recent);
auto h = countHeight();
if (h != height()) {
resize(width(), h);
@ -672,14 +739,14 @@ void EmojiListWidget::setSelected(int newSelected) {
}
}
void EmojiListWidget::showEmojiSection(DBIEmojiSection section) {
void EmojiListWidget::showEmojiSection(Section section) {
clearSelection();
refreshRecent();
auto y = 0;
enumerateSections([&y, sectionForSearch = section](const SectionInfo &info) {
if (EmojiSectionAtIndex(info.section) == sectionForSearch) {
if (static_cast<Section>(info.section) == sectionForSearch) {
y = info.top;
return false;
}

View File

@ -25,9 +25,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace ChatHelpers {
constexpr auto kEmojiSectionCount = 8;
inline DBIEmojiSection EmojiSectionAtIndex(int index) {
return (index < 0 || index >= kEmojiSectionCount) ? dbiesRecent : DBIEmojiSection(index - 1);
}
class EmojiColorPicker : public TWidget {
Q_OBJECT
@ -89,13 +86,15 @@ class EmojiListWidget : public EmojiPanel::Inner {
public:
EmojiListWidget(QWidget *parent);
void refreshRecent() override;
void hideFinish(bool completely) override;
void clearSelection() override;
object_ptr<TWidget> createController() override;
using Section = Ui::Emoji::Section;
void showEmojiSection(DBIEmojiSection section);
DBIEmojiSection currentSection(int yOffset) const;
void setVisibleTopBottom(int visibleTop, int visibleBottom) override;
void refreshRecent() override;
void clearSelection() override;
object_ptr<EmojiPanel::InnerFooter> createFooter() override;
void showEmojiSection(Section section);
Section currentSection(int yOffset) const;
public slots:
void onShowPicker();
@ -117,10 +116,13 @@ protected:
void leaveToChildEvent(QEvent *e, QWidget *child) override;
void enterFromChildEvent(QEvent *e, QWidget *child) override;
bool event(QEvent *e) override;
EmojiPanel::InnerFooter *getFooter() const override;
void processHideFinished() override;
int countHeight() override;
private:
class Controller;
class Footer;
struct SectionInfo {
int section = 0;
@ -144,6 +146,8 @@ private:
QRect emojiRect(int section, int sel);
Footer *_footer = nullptr;
int _counts[kEmojiSectionCount];
QVector<EmojiPtr> _emoji[kEmojiSectionCount];

View File

@ -28,11 +28,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/widgets/shadow.h"
#include "ui/widgets/discrete_sliders.h"
#include "ui/widgets/scroll_area.h"
#include "boxes/stickersetbox.h"
#include "boxes/stickers_box.h"
#include "boxes/confirmbox.h"
#include "inline_bots/inline_bot_result.h"
#include "dialogs/dialogs_layout.h"
#include "stickers/stickers.h"
#include "storage/localstorage.h"
#include "lang.h"
@ -266,7 +262,9 @@ void EmojiPanel::SlideAnimation::paintFrame(QPainter &p, float64 dt, float64 opa
EmojiPanel::Tab::Tab(TabType type, object_ptr<Inner> widget)
: _type(type)
, _widget(std::move(widget))
, _weak(_widget) {
, _weak(_widget)
, _footer(_widget->createFooter()) {
_footer->setParent(_widget->parentWidget());
}
object_ptr<EmojiPanel::Inner> EmojiPanel::Tab::takeWidget() {
@ -283,15 +281,6 @@ void EmojiPanel::Tab::saveScrollTop() {
}
EmojiPanel::EmojiPanel(QWidget *parent) : TWidget(parent)
, _recent(this, st::emojiCategoryRecent)
, _people(this, st::emojiCategoryPeople)
, _nature(this, st::emojiCategoryNature)
, _food(this, st::emojiCategoryFood)
, _activity(this, st::emojiCategoryActivity)
, _travel(this, st::emojiCategoryTravel)
, _objects(this, st::emojiCategoryObjects)
, _symbols(this, st::emojiCategorySymbols)
, _a_icons(animation(this, &EmojiPanel::step_icons))
, _scroll(this, st::emojiScroll)
, _tabsSlider(this, st::emojiTabs)
, _topShadow(this, st::shadowFg)
@ -317,26 +306,13 @@ EmojiPanel::EmojiPanel(QWidget *parent) : TWidget(parent)
_bottomShadow->setGeometry(_tabsSlider->x(), _scroll->y() + _scroll->height() - st::lineWidth, _tabsSlider->width(), st::lineWidth);
int32 left = _iconsLeft = innerRect().x() + (st::emojiPanWidth - 8 * st::emojiCategory.width) / 2;
int32 top = _iconsTop = innerRect().y() + innerRect().height() - st::emojiCategory.height;
prepareSection(left, top, _width, _recent, dbiesRecent);
prepareSection(left, top, _width, _people, dbiesPeople);
prepareSection(left, top, _width, _nature, dbiesNature);
prepareSection(left, top, _width, _food, dbiesFood);
prepareSection(left, top, _width, _activity, dbiesActivity);
prepareSection(left, top, _width, _travel, dbiesTravel);
prepareSection(left, top, _width, _objects, dbiesObjects);
prepareSection(left, top, _width, _symbols, dbiesSymbols);
setCurrentSectionIcon(dbiesRecent);
_hideTimer.setSingleShot(true);
connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideByTimerOrLeave()));
for (auto &tab : _tabs) {
connect(tab.widget(), &Inner::scrollToY, this, [this, tab = &tab](int y) {
if (tab == currentTab()) {
_scroll->scrollToY(y);
scrollToY(y);
} else {
tab->saveScrollTop(y);
}
@ -353,17 +329,12 @@ EmojiPanel::EmojiPanel(QWidget *parent) : TWidget(parent)
connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
connect(emoji(), SIGNAL(selected(EmojiPtr)), this, SIGNAL(emojiSelected(EmojiPtr)));
connect(stickers(), SIGNAL(selected(DocumentData*)), this, SIGNAL(stickerSelected(DocumentData*)));
connect(stickers(), SIGNAL(checkForHide()), this, SLOT(onCheckForHide()));
connect(gifs(), SIGNAL(selected(DocumentData*)), this, SIGNAL(stickerSelected(DocumentData*)));
connect(gifs(), SIGNAL(selected(PhotoData*)), this, SIGNAL(photoSelected(PhotoData*)));
connect(gifs(), SIGNAL(selected(InlineBots::Result*, UserData*)), this, SIGNAL(inlineResultSelected(InlineBots::Result*, UserData*)));
connect(gifs(), SIGNAL(emptyInlineRows()), this, SLOT(onEmptyInlineRows()));
connect(stickers(), SIGNAL(displaySet(quint64)), this, SLOT(onDisplaySet(quint64)));
connect(stickers(), SIGNAL(installSet(quint64)), this, SLOT(onInstallSet(quint64)));
connect(stickers(), SIGNAL(removeSet(quint64)), this, SLOT(onRemoveSet(quint64)));
connect(stickers(), SIGNAL(refreshIcons(bool)), this, SLOT(onRefreshIcons(bool)));
_saveConfigTimer.setSingleShot(true);
connect(&_saveConfigTimer, SIGNAL(timeout()), this, SLOT(onSaveConfig()));
@ -379,7 +350,6 @@ EmojiPanel::EmojiPanel(QWidget *parent) : TWidget(parent)
_bottomShadow->raise();
_tabsSlider->raise();
setMouseTracking(true);
// setAttribute(Qt::WA_AcceptTouchEvents);
setAttribute(Qt::WA_OpaquePaintEvent, false);
@ -430,25 +400,14 @@ void EmojiPanel::updateContentHeight() {
}
_bottomShadow->setGeometry(_tabsSlider->x(), _scroll->y() + _scroll->height() - st::lineWidth, _tabsSlider->width(), st::lineWidth);
_iconsTop = innerRect().y() + innerRect().height() - st::emojiCategory.height;
_recent->move(_recent->x(), _iconsTop);
_people->move(_people->x(), _iconsTop);
_nature->move(_nature->x(), _iconsTop);
_food->move(_food->x(), _iconsTop);
_activity->move(_activity->x(), _iconsTop);
_travel->move(_travel->x(), _iconsTop);
_objects->move(_objects->x(), _iconsTop);
_symbols->move(_symbols->x(), _iconsTop);
_footerTop = innerRect().y() + innerRect().height() - st::emojiCategory.height;
for (auto &tab : _tabs) {
tab.footer()->move(_tabsSlider->x(), _footerTop);
}
update();
}
void EmojiPanel::prepareSection(int &left, int top, int _width, Ui::IconButton *sectionIcon, DBIEmojiSection value) {
sectionIcon->moveToLeft(left, top, _width);
left += sectionIcon->width();
sectionIcon->setClickedCallback([this, value] { setActiveSection(value); });
}
void EmojiPanel::onWndActiveChanged() {
if (!App::wnd()->windowHandle()->isActive() && !isHidden()) {
leaveEvent(0);
@ -463,23 +422,6 @@ void EmojiPanel::onSaveConfigDelayed(int delay) {
_saveConfigTimer.start(delay);
}
void EmojiPanel::paintStickerSettingsIcon(Painter &p) const {
int settingsLeft = _iconsLeft + 7 * st::emojiCategory.width;
st::stickersSettings.paint(p, settingsLeft + st::emojiCategory.iconPosition.x(), _iconsTop + st::emojiCategory.iconPosition.y(), width());
}
void EmojiPanel::paintFeaturedStickerSetsBadge(Painter &p, int iconLeft) const {
if (auto unread = Global::FeaturedStickerSetsUnreadCount()) {
Dialogs::Layout::UnreadBadgeStyle unreadSt;
unreadSt.sizeId = Dialogs::Layout::UnreadBadgeInStickersPanel;
unreadSt.size = st::stickersSettingsUnreadSize;
int unreadRight = iconLeft + st::emojiCategory.width - st::stickersSettingsUnreadPosition.x();
if (rtl()) unreadRight = width() - unreadRight;
int unreadTop = _iconsTop + st::stickersSettingsUnreadPosition.y();
Dialogs::Layout::paintUnreadCount(p, QString::number(unread), unreadRight, unreadTop, unreadSt);
}
}
void EmojiPanel::paintEvent(QPaintEvent *e) {
Painter p(this);
@ -544,7 +486,7 @@ void EmojiPanel::paintContent(Painter &p) {
App::roundRect(p, topPart, st::emojiPanBg, ImageRoundRadius::Small, App::RectPart::TopFull | App::RectPart::NoTopBottom);
auto showSectionIcons = (_currentTabType != TabType::Gifs);
auto bottomPart = QRect(inner.x(), _iconsTop - st::buttonRadius, inner.width(), st::emojiCategory.height + st::buttonRadius);
auto bottomPart = QRect(inner.x(), _footerTop - st::buttonRadius, inner.width(), st::emojiCategory.height + st::buttonRadius);
auto &bottomBg = showSectionIcons ? st::emojiPanCategories : st::emojiPanBg;
auto bottomParts = App::RectPart::NoTopBottom | App::RectPart::BottomFull;
App::roundRect(p, bottomPart, bottomBg, ImageRoundRadius::Small, bottomParts);
@ -554,81 +496,6 @@ void EmojiPanel::paintContent(Painter &p) {
auto sidesHeight = _scroll->y() + _scroll->height() - sidesTop;
p.fillRect(myrtlrect(inner.x() + inner.width() - st::emojiScroll.width, sidesTop, st::emojiScroll.width, sidesHeight), st::emojiPanBg);
p.fillRect(myrtlrect(inner.x(), sidesTop, st::buttonRadius, sidesHeight), st::emojiPanBg);
switch (_currentTabType) {
case TabType::Emoji: {
auto vertical = verticalRect();
p.fillRect(vertical.x(), _iconsTop, vertical.width(), st::emojiCategory.height - st::buttonRadius, st::emojiPanCategories);
} break;
case TabType::Stickers: {
paintStickerSettingsIcon(p);
if (!_icons.isEmpty()) {
auto x = _iconsLeft;
auto selxrel = _iconsLeft + qRound(_iconSelX.current());
auto selx = selxrel - qRound(_iconsX.current());
QRect clip(x, _iconsTop, _iconsLeft + 7 * st::emojiCategory.width - x, st::emojiCategory.height);
if (rtl()) clip.moveLeft(width() - x - clip.width());
p.setClipRect(clip);
auto getSpecialSetIcon = [](uint64 setId, bool active) {
if (setId == Stickers::FeaturedSetId) {
return active ? &st::stickersTrendingActive : &st::stickersTrending;
}
return active ? &st::emojiRecentActive : &st::emojiRecent;
};
int i = 0;
auto iconsX = qRound(_iconsX.current());
i += iconsX / int(st::emojiCategory.width);
x -= iconsX % int(st::emojiCategory.width);
selxrel -= iconsX;
for (int l = qMin(_icons.size(), i + 8); i < l; ++i) {
auto &s = _icons.at(i);
if (s.sticker) {
s.sticker->thumb->load();
QPixmap pix(s.sticker->thumb->pix(s.pixw, s.pixh));
p.drawPixmapLeft(x + (st::emojiCategory.width - s.pixw) / 2, _iconsTop + (st::emojiCategory.height - s.pixh) / 2, width(), pix);
x += st::emojiCategory.width;
} else {
getSpecialSetIcon(s.setId, false)->paint(p, x + st::emojiCategory.iconPosition.x(), _iconsTop + st::emojiCategory.iconPosition.y(), width());
if (s.setId == Stickers::FeaturedSetId) {
paintFeaturedStickerSetsBadge(p, x);
}
x += st::emojiCategory.width;
}
}
if (rtl()) selx = width() - selx - st::emojiCategory.width;
p.fillRect(selx, _iconsTop + st::emojiCategory.height - st::stickerIconPadding, st::emojiCategory.width, st::stickerIconSel, st::stickerIconSelColor);
auto o_left = snap(_iconsX.current() / st::stickerIconLeft.width(), 0., 1.);
if (o_left > 0) {
p.setOpacity(o_left);
st::stickerIconLeft.fill(p, rtlrect(_iconsLeft, _iconsTop, st::stickerIconLeft.width(), st::emojiCategory.height, width()));
p.setOpacity(1.);
}
auto o_right = snap((_iconsMax - _iconsX.current()) / st::stickerIconRight.width(), 0., 1.);
if (o_right > 0) {
p.setOpacity(o_right);
st::stickerIconRight.fill(p, rtlrect(_iconsLeft + 7 * st::emojiCategory.width - st::stickerIconRight.width(), _iconsTop, st::stickerIconRight.width(), st::emojiCategory.height, width()));
p.setOpacity(1.);
}
p.setClipRect(QRect());
}
} break;
case TabType::Gifs: {
p.fillRect(myrtlrect(inner.x() + inner.width() - st::emojiScroll.width, _iconsTop, st::emojiScroll.width, st::emojiCategory.height - st::buttonRadius), st::emojiPanBg);
p.fillRect(myrtlrect(inner.x(), _iconsTop, st::buttonRadius, st::emojiCategory.height - st::buttonRadius), st::emojiPanBg);
} break;
default: Unexpected("Bad tab type.");
}
}
int EmojiPanel::marginTop() const {
@ -653,7 +520,7 @@ void EmojiPanel::enterEventHook(QEvent *e) {
}
bool EmojiPanel::preventAutoHide() const {
return _removingSetId || _displayingSetId;
return stickers()->preventAutoHide();
}
void EmojiPanel::leaveEventHook(QEvent *e) {
@ -686,101 +553,6 @@ void EmojiPanel::otherLeave() {
}
}
void EmojiPanel::mousePressEvent(QMouseEvent *e) {
if (_currentTabType != TabType::Stickers || e->button() != Qt::LeftButton) {
return;
}
_iconsMousePos = e ? e->globalPos() : QCursor::pos();
updateSelected();
if (_iconOver == _icons.size()) {
Ui::show(Box<StickersBox>(StickersBox::Section::Installed));
} else {
_iconDown = _iconOver;
_iconsMouseDown = _iconsMousePos;
_iconsStartX = qRound(_iconsX.current());
}
}
void EmojiPanel::mouseMoveEvent(QMouseEvent *e) {
if (_currentTabType != TabType::Stickers) {
return;
}
_iconsMousePos = e ? e->globalPos() : QCursor::pos();
updateSelected();
if (!_iconsDragging && !_icons.isEmpty() && _iconDown >= 0) {
if ((_iconsMousePos - _iconsMouseDown).manhattanLength() >= QApplication::startDragDistance()) {
_iconsDragging = true;
}
}
if (_iconsDragging) {
auto newX = snap(_iconsStartX + (rtl() ? -1 : 1) * (_iconsMouseDown.x() - _iconsMousePos.x()), 0, _iconsMax);
if (newX != qRound(_iconsX.current())) {
_iconsX = anim::value(newX, newX);
_iconsStartAnim = 0;
_a_icons.stop();
updateIcons();
}
}
}
void EmojiPanel::mouseReleaseEvent(QMouseEvent *e) {
if (_currentTabType != TabType::Stickers || _icons.isEmpty()) {
return;
}
auto wasDown = _iconDown;
_iconDown = -1;
_iconsMousePos = e ? e->globalPos() : QCursor::pos();
if (_iconsDragging) {
auto newX = snap(_iconsStartX + _iconsMouseDown.x() - _iconsMousePos.x(), 0, _iconsMax);
if (newX != qRound(_iconsX.current())) {
_iconsX = anim::value(newX, newX);
_iconsStartAnim = 0;
_a_icons.stop();
updateIcons();
}
_iconsDragging = false;
updateSelected();
} else {
updateSelected();
if (wasDown == _iconOver && _iconOver >= 0 && _iconOver < _icons.size()) {
_iconSelX = anim::value(_iconOver * st::emojiCategory.width, _iconOver * st::emojiCategory.width);
stickers()->showStickerSet(_icons[_iconOver].setId);
}
}
}
bool EmojiPanel::event(QEvent *e) {
if (e->type() == QEvent::TouchBegin) {
} else if (e->type() == QEvent::Wheel) {
if (!_icons.isEmpty() && _iconOver >= 0 && _iconOver < _icons.size() && _iconDown < 0) {
QWheelEvent *ev = static_cast<QWheelEvent*>(e);
bool hor = (ev->angleDelta().x() != 0 || ev->orientation() == Qt::Horizontal);
bool ver = (ev->angleDelta().y() != 0 || ev->orientation() == Qt::Vertical);
if (hor) _horizontal = true;
auto newX = qRound(_iconsX.current());
if (/*_horizontal && */hor) {
newX = snap(newX - (rtl() ? -1 : 1) * (ev->pixelDelta().x() ? ev->pixelDelta().x() : ev->angleDelta().x()), 0, _iconsMax);
} else if (/*!_horizontal && */ver) {
newX = snap(newX - (ev->pixelDelta().y() ? ev->pixelDelta().y() : ev->angleDelta().y()), 0, _iconsMax);
}
if (newX != qRound(_iconsX.current())) {
_iconsX = anim::value(newX, newX);
_iconsStartAnim = 0;
_a_icons.stop();
updateSelected();
updateIcons();
}
}
}
return TWidget::event(e);
}
void EmojiPanel::hideFast() {
if (isHidden()) return;
@ -806,99 +578,6 @@ void EmojiPanel::refreshSavedGifs() {
update();
}
void EmojiPanel::onRefreshIcons(bool scrollAnimation) {
_iconOver = -1;
stickers()->fillIcons(_icons);
_iconsX.finish();
_iconSelX.finish();
_iconsStartAnim = 0;
_a_icons.stop();
if (_icons.isEmpty()) {
_iconsMax = 0;
} else {
_iconsMax = qMax(int((_icons.size() - 7) * st::emojiCategory.width), 0);
}
if (_iconsX.current() > _iconsMax) {
_iconsX = anim::value(_iconsMax, _iconsMax);
}
updateSelected();
if (_currentTabType == TabType::Stickers) {
validateSelectedIcon(scrollAnimation ? ValidateIconAnimations::Scroll : ValidateIconAnimations::None);
updateContentHeight();
}
updateIcons();
}
void EmojiPanel::leaveToChildEvent(QEvent *e, QWidget *child) {
if (_currentTabType != TabType::Stickers) {
return;
}
_iconsMousePos = QCursor::pos();
updateSelected();
}
void EmojiPanel::updateSelected() {
if (_iconDown >= 0) {
return;
}
QPoint p(mapFromGlobal(_iconsMousePos));
int32 x = p.x(), y = p.y(), newOver = -1;
if (rtl()) x = width() - x;
x -= _iconsLeft;
if (x >= st::emojiCategory.width * 7 && x < st::emojiCategory.width * 8 && y >= _iconsTop && y < _iconsTop + st::emojiCategory.height) {
newOver = _icons.size();
} else if (!_icons.isEmpty()) {
if (y >= _iconsTop && y < _iconsTop + st::emojiCategory.height && x >= 0 && x < 7 * st::emojiCategory.width && x < _icons.size() * st::emojiCategory.width) {
x += qRound(_iconsX.current());
newOver = qFloor(x / st::emojiCategory.width);
}
}
if (newOver != _iconOver) {
if (newOver < 0) {
setCursor(style::cur_default);
} else if (_iconOver < 0) {
setCursor(style::cur_pointer);
}
_iconOver = newOver;
}
}
void EmojiPanel::updateIcons() {
if (_currentTabType != TabType::Stickers) {
return;
}
auto verticalInner = rect().marginsRemoved(st::emojiPanMargins).marginsRemoved(QMargins(st::buttonRadius, 0, st::buttonRadius, 0));
update(verticalInner.left(), _iconsTop, verticalInner.width(), st::emojiCategory.height);
}
void EmojiPanel::step_icons(TimeMs ms, bool timer) {
if (_currentTabType != TabType::Stickers) {
_a_icons.stop();
return;
}
if (_iconsStartAnim) {
float64 dt = (ms - _iconsStartAnim) / float64(st::stickerIconMove);
if (dt >= 1) {
_iconsStartAnim = 0;
_iconsX.finish();
_iconSelX.finish();
} else {
_iconsX.update(dt, anim::linear);
_iconSelX.update(dt, anim::linear);
}
if (timer) updateSelected();
}
if (timer) updateIcons();
if (!_iconsStartAnim) {
_a_icons.stop();
}
}
void EmojiPanel::opacityAnimationCallback() {
update();
if (!_a_opacity.animating()) {
@ -1004,23 +683,17 @@ EmojiPanel::~EmojiPanel() = default;
void EmojiPanel::hideFinished() {
hide();
currentTab()->widget()->hideFinish(true);
for (auto &tab : _tabs) {
tab.widget()->panelHideFinished();
}
_a_show.finish();
_showAnimation.reset();
_a_slide.finish();
_slideAnimation.reset();
_cache = QPixmap();
_horizontal = false;
_hiding = false;
_scroll->scrollToY(0);
setCurrentSectionIcon(dbiesRecent);
_iconOver = _iconDown = -1;
_iconSel = 0;
_iconsX = anim::value();
_iconSelX = anim::value();
_iconsStartAnim = 0;
_a_icons.stop();
scrollToY(0);
Notify::clipStopperHidden(ClipStopperSavedGifsPanel);
}
@ -1079,25 +752,7 @@ bool EmojiPanel::ui_isInlineItemBeingChosen() {
}
void EmojiPanel::showAll() {
if (_currentTabType == TabType::Emoji) {
_recent->show();
_people->show();
_nature->show();
_food->show();
_activity->show();
_travel->show();
_objects->show();
_symbols->show();
} else {
_recent->hide();
_people->hide();
_nature->hide();
_food->hide();
_activity->hide();
_travel->hide();
_objects->hide();
_symbols->hide();
}
currentTab()->footer()->show();
_scroll->show();
_topShadow->show();
_bottomShadow->setVisible(_currentTabType == TabType::Gifs);
@ -1111,74 +766,10 @@ void EmojiPanel::hideForSliding() {
currentTab()->widget()->clearSelection();
}
void EmojiPanel::setActiveSection(DBIEmojiSection tab) {
emoji()->showEmojiSection(tab);
}
void EmojiPanel::onScroll() {
auto scrollTop = _scroll->scrollTop();
auto scrollBottom = scrollTop + _scroll->height();
currentTab()->widget()->setVisibleTopBottom(scrollTop, scrollBottom);
switch (_currentTabType) {
case TabType::Emoji: {
setCurrentSectionIcon(emoji()->currentSection(scrollTop));
} break;
case TabType::Stickers: {
validateSelectedIcon(ValidateIconAnimations::Full);
} break;
case TabType::Gifs: {
if (scrollBottom > _scroll->scrollTopMax()) {
onInlineRequest();
}
} break;
default: Unexpected("Bad type value.");
}
}
void EmojiPanel::setCurrentSectionIcon(DBIEmojiSection section) {
_recent->setIconOverride((section == dbiesRecent) ? &st::emojiRecentActive : nullptr);
_people->setIconOverride((section == dbiesPeople) ? &st::emojiPeopleActive : nullptr);
_nature->setIconOverride((section == dbiesNature) ? &st::emojiNatureActive : nullptr);
_food->setIconOverride((section == dbiesFood) ? &st::emojiFoodActive : nullptr);
_activity->setIconOverride((section == dbiesActivity) ? &st::emojiActivityActive : nullptr);
_travel->setIconOverride((section == dbiesTravel) ? &st::emojiTravelActive : nullptr);
_objects->setIconOverride((section == dbiesObjects) ? &st::emojiObjectsActive : nullptr);
_symbols->setIconOverride((section == dbiesSymbols) ? &st::emojiSymbolsActive : nullptr);
}
void EmojiPanel::validateSelectedIcon(ValidateIconAnimations animations) {
auto setId = stickers()->currentSet(_scroll->scrollTop());
auto newSel = 0;
for (auto i = 0, l = _icons.size(); i != l; ++i) {
if (_icons[i].setId == setId) {
newSel = i;
break;
}
}
if (newSel != _iconSel) {
_iconSel = newSel;
auto iconSelXFinal = newSel * st::emojiCategory.width;
if (animations == ValidateIconAnimations::Full) {
_iconSelX.start(iconSelXFinal);
} else {
_iconSelX = anim::value(iconSelXFinal, iconSelXFinal);
}
auto iconsXFinal = snap((2 * newSel - 7) * int(st::emojiCategory.width) / 2, 0, _iconsMax);
if (animations == ValidateIconAnimations::None) {
_iconsX = anim::value(iconsXFinal, iconsXFinal);
_a_icons.stop();
} else {
_iconsX.start(iconsXFinal);
_iconsStartAnim = getms();
_a_icons.start();
}
updateSelected();
updateIcons();
}
}
style::margins EmojiPanel::innerPadding() const {
@ -1230,19 +821,13 @@ void EmojiPanel::switchTab() {
auto widget = _scroll->takeWidget<Inner>();
widget->setParent(this);
widget->hide();
currentTab()->footer()->hide();
currentTab()->returnWidget(std::move(widget));
_currentTabType = newTabType;
if (_currentTabType != TabType::Gifs) {
if (_currentTabType == TabType::Gifs) {
Notify::clipStopperHidden(ClipStopperSavedGifsPanel);
}
if (_currentTabType == TabType::Stickers) {
validateSelectedIcon(ValidateIconAnimations::None);
}
updateContentHeight();
_iconOver = -1;
_a_icons.stop();
setWidgetToScrollArea();
auto nowCache = grabForComplexAnimation(GrabType::Slide);
@ -1261,7 +846,7 @@ void EmojiPanel::switchTab() {
hideForSliding();
getTab(wasTab)->widget()->hideFinish(false);
getTab(wasTab)->widget()->hideFinished();
_a_slide.start([this] { update(); }, 0., 1., st::emojiPanSlideDuration, anim::linear);
update();
@ -1287,88 +872,15 @@ void EmojiPanel::setWidgetToScrollArea() {
_scroll->disableScroll(false);
currentTab()->widget()->moveToLeft(0, 0);
currentTab()->widget()->show();
_scroll->scrollToY(currentTab()->getScrollTop());
scrollToY(currentTab()->getScrollTop());
updateContentHeight();
onScroll();
}
void EmojiPanel::onDisplaySet(quint64 setId) {
auto &sets = Global::StickerSets();
auto it = sets.constFind(setId);
if (it != sets.cend()) {
_displayingSetId = setId;
auto box = Ui::show(Box<StickerSetBox>(Stickers::inputSetId(*it)), KeepOtherLayers);
connect(box, SIGNAL(destroyed(QObject*)), this, SLOT(onDelayedHide()));
}
}
void EmojiPanel::onInstallSet(quint64 setId) {
auto &sets = Global::StickerSets();
auto it = sets.constFind(setId);
if (it != sets.cend()) {
request(MTPmessages_InstallStickerSet(Stickers::inputSetId(*it), MTP_bool(false))).done([this](const MTPmessages_StickerSetInstallResult &result) {
if (result.type() == mtpc_messages_stickerSetInstallResultArchive) {
Stickers::applyArchivedResult(result.c_messages_stickerSetInstallResultArchive());
}
}).fail([this, setId](const RPCError &error) {
stickers()->notInstalledLocally(setId);
Stickers::undoInstallLocally(setId);
}).send();
stickers()->installedLocally(setId);
Stickers::installLocally(setId);
}
}
void EmojiPanel::onRemoveSet(quint64 setId) {
auto &sets = Global::StickerSets();
auto it = sets.constFind(setId);
if (it != sets.cend() && !(it->flags & MTPDstickerSet::Flag::f_official)) {
_removingSetId = it->id;
auto text = lng_stickers_remove_pack(lt_sticker_pack, it->title);
Ui::show(Box<ConfirmBox>(text, lang(lng_box_remove), base::lambda_guarded(this, [this] {
Ui::hideLayer();
auto &sets = Global::RefStickerSets();
auto it = sets.find(_removingSetId);
if (it != sets.cend() && !(it->flags & MTPDstickerSet::Flag::f_official)) {
if (it->id && it->access) {
request(MTPmessages_UninstallStickerSet(MTP_inputStickerSetID(MTP_long(it->id), MTP_long(it->access)))).send();
} else if (!it->shortName.isEmpty()) {
request(MTPmessages_UninstallStickerSet(MTP_inputStickerSetShortName(MTP_string(it->shortName)))).send();
}
bool writeRecent = false;
RecentStickerPack &recent(cGetRecentStickers());
for (RecentStickerPack::iterator i = recent.begin(); i != recent.cend();) {
if (it->stickers.indexOf(i->first) >= 0) {
i = recent.erase(i);
writeRecent = true;
} else {
++i;
}
}
it->flags &= ~MTPDstickerSet::Flag::f_installed;
if (!(it->flags & MTPDstickerSet_ClientFlag::f_featured) && !(it->flags & MTPDstickerSet_ClientFlag::f_special)) {
sets.erase(it);
}
int removeIndex = Global::StickerSetsOrder().indexOf(_removingSetId);
if (removeIndex >= 0) Global::RefStickerSetsOrder().removeAt(removeIndex);
refreshStickers();
Local::writeInstalledStickers();
if (writeRecent) Local::writeUserSettings();
}
_removingSetId = 0;
onDelayedHide();
}), base::lambda_guarded(this, [this] {
onDelayedHide();
})));
}
}
void EmojiPanel::onDelayedHide() {
void EmojiPanel::onCheckForHide() {
if (!rect().contains(mapFromGlobal(QCursor::pos()))) {
_hideTimer.start(3000);
}
_removingSetId = 0;
_displayingSetId = 0;
}
void EmojiPanel::clearInlineBot() {
@ -1525,11 +1037,18 @@ bool EmojiPanel::refreshInlineRows(int32 *added) {
return (entry != nullptr);
}
void EmojiPanel::scrollToY(int y) {
_scroll->scrollToY(y);
// Qt render glitch workaround, shadow sometimes disappears if we just scroll to y.
_topShadow->update();
}
int32 EmojiPanel::showInlineRows(bool newResults) {
int32 added = 0;
bool clear = !refreshInlineRows(&added);
if (newResults) {
_scroll->scrollToY(0);
scrollToY(0);
}
auto hidden = isHidden();
@ -1560,4 +1079,23 @@ void EmojiPanel::Inner::setVisibleTopBottom(int visibleTop, int visibleBottom) {
}
}
void EmojiPanel::Inner::hideFinished() {
processHideFinished();
if (auto footer = getFooter()) {
footer->processHideFinished();
}
}
void EmojiPanel::Inner::panelHideFinished() {
hideFinished();
processPanelHideFinished();
if (auto footer = getFooter()) {
footer->processPanelHideFinished();
}
}
EmojiPanel::InnerFooter::InnerFooter(QWidget *parent) : TWidget(parent) {
resize(st::emojiPanWidth, st::emojiCategory.height);
}
} // namespace ChatHelpers

View File

@ -21,7 +21,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#pragma once
#include "ui/twidget.h"
#include "ui/abstract_button.h"
#include "ui/effects/panel_animation.h"
#include "mtproto/sender.h"
#include "inline_bots/inline_bot_layout_item.h"
@ -37,9 +36,6 @@ class Result;
namespace Ui {
class PlainShadow;
class ScrollArea;
class IconButton;
class LinkButton;
class RoundButton;
class RippleAnimation;
class SettingsSlider;
} // namesapce Ui
@ -60,18 +56,6 @@ struct InlineCacheEntry {
InlineResults results;
};
struct StickerIcon {
StickerIcon(uint64 setId) : setId(setId) {
}
StickerIcon(uint64 setId, DocumentData *sticker, int32 pixw, int32 pixh) : setId(setId), sticker(sticker), pixw(pixw), pixh(pixh) {
}
uint64 setId = 0;
DocumentData *sticker = nullptr;
int pixw = 0;
int pixh = 0;
};
class EmojiPanel : public TWidget, private MTP::Sender {
Q_OBJECT
@ -87,10 +71,6 @@ public:
return _hiding || _hideTimer.isActive();
}
void step_icons(TimeMs ms, bool timer);
void leaveToChildEvent(QEvent *e, QWidget *child) override;
void stickersInstalled(uint64 setId);
void queryInlineBot(UserData *bot, PeerData *peer, QString query);
@ -106,6 +86,7 @@ public:
~EmojiPanel();
class Inner;
class InnerFooter;
protected:
void enterEventHook(QEvent *e) override;
@ -113,30 +94,20 @@ protected:
void otherEnter();
void otherLeave();
void mousePressEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void paintEvent(QPaintEvent *e) override;
bool event(QEvent *e) override;
bool eventFilter(QObject *obj, QEvent *e) override;
public slots:
void refreshStickers();
private slots:
private slots:
void hideByTimerOrLeave();
void refreshSavedGifs();
void onWndActiveChanged();
void onScroll();
void onDisplaySet(quint64 setId);
void onInstallSet(quint64 setId);
void onRemoveSet(quint64 setId);
void onDelayedHide();
void onRefreshIcons(bool scrollAnimation);
void onCheckForHide();
void onSaveConfig();
void onSaveConfigDelayed(int delay);
@ -169,6 +140,9 @@ private:
gsl::not_null<Inner*> widget() const {
return _weak;
}
gsl::not_null<InnerFooter*> footer() const {
return _footer;
}
void saveScrollTop();
void saveScrollTop(int scrollTop) {
@ -182,6 +156,7 @@ private:
TabType _type = TabType::Emoji;
object_ptr<Inner> _widget = { nullptr };
QPointer<Inner> _weak;
object_ptr<InnerFooter> _footer;
int _scrollTop = 0;
};
@ -215,31 +190,16 @@ private:
void startOpacityAnimation(bool hiding);
void prepareCache();
void scrollToY(int y);
void opacityAnimationCallback();
void hideFinished();
void showStarted();
bool preventAutoHide() const;
void setActiveSection(DBIEmojiSection section);
void setCurrentSectionIcon(DBIEmojiSection section);
void paintStickerSettingsIcon(Painter &p) const;
void paintFeaturedStickerSetsBadge(Painter &p, int iconLeft) const;
enum class ValidateIconAnimations {
Full,
Scroll,
None,
};
void validateSelectedIcon(ValidateIconAnimations animations);
void updateContentHeight();
void updateSelected();
void updateIcons();
void prepareSection(int &left, int top, int _width, Ui::IconButton *sectionIcon, DBIEmojiSection section);
void showAll();
void hideForSliding();
@ -266,11 +226,11 @@ private:
int _minBottom = 0;
int _contentMaxHeight = 0;
int _contentHeight = 0;
bool _horizontal = false;
int _width = 0;
int _height = 0;
int _bottom = 0;
int _footerTop = 0;
std::unique_ptr<Ui::PanelAnimation> _showAnimation;
Animation _a_show;
@ -286,30 +246,6 @@ private:
std::unique_ptr<SlideAnimation> _slideAnimation;
Animation _a_slide;
object_ptr<Ui::IconButton> _recent;
object_ptr<Ui::IconButton> _people;
object_ptr<Ui::IconButton> _nature;
object_ptr<Ui::IconButton> _food;
object_ptr<Ui::IconButton> _activity;
object_ptr<Ui::IconButton> _travel;
object_ptr<Ui::IconButton> _objects;
object_ptr<Ui::IconButton> _symbols;
QList<StickerIcon> _icons;
int _iconOver = -1;
int _iconSel = 0;
int _iconDown = -1;
bool _iconsDragging = false;
BasicAnimation _a_icons;
QPoint _iconsMousePos, _iconsMouseDown;
int _iconsLeft = 0;
int _iconsTop = 0;
int _iconsStartX = 0;
int _iconsMax = 0;
anim::value _iconsX;
anim::value _iconSelX;
TimeMs _iconsStartAnim = 0;
object_ptr<Ui::SettingsSlider> _tabsSlider = { nullptr };
object_ptr<Ui::PlainShadow> _topShadow;
object_ptr<Ui::PlainShadow> _bottomShadow;
@ -317,9 +253,6 @@ private:
std::array<Tab, Tab::kCount> _tabs;
TabType _currentTabType = TabType::Emoji;
uint64 _displayingSetId = 0;
uint64 _removingSetId = 0;
QTimer _saveConfigTimer;
// inline bots
@ -355,10 +288,11 @@ public:
virtual void refreshRecent() = 0;
virtual void preloadImages() {
}
virtual void hideFinish(bool completely) = 0;
void hideFinished();
void panelHideFinished();
virtual void clearSelection() = 0;
virtual object_ptr<TWidget> createController() = 0;
virtual object_ptr<InnerFooter> createFooter() = 0;
signals:
void scrollToY(int y);
@ -367,6 +301,11 @@ signals:
protected:
virtual int countHeight() = 0;
virtual InnerFooter *getFooter() const = 0;
virtual void processHideFinished() {
}
virtual void processPanelHideFinished() {
}
private:
int _visibleTop = 0;
@ -374,4 +313,17 @@ private:
};
class EmojiPanel::InnerFooter : public TWidget {
public:
InnerFooter(QWidget *parent);
protected:
virtual void processHideFinished() {
}
virtual void processPanelHideFinished() {
}
friend class Inner;
};
} // namespace ChatHelpers

View File

@ -39,19 +39,26 @@ constexpr auto kInlineItemsMaxPerRow = 5;
} // namespace
class GifsListWidget::Controller : public TWidget {
class GifsListWidget::Footer : public EmojiPanel::InnerFooter {
public:
Controller(gsl::not_null<GifsListWidget*> parent);
Footer(gsl::not_null<GifsListWidget*> parent);
protected:
void processPanelHideFinished() override;
private:
gsl::not_null<GifsListWidget*> _pan;
};
GifsListWidget::Controller::Controller(gsl::not_null<GifsListWidget*> parent) : TWidget(parent)
GifsListWidget::Footer::Footer(gsl::not_null<GifsListWidget*> parent) : InnerFooter(parent)
, _pan(parent) {
}
void GifsListWidget::Footer::processPanelHideFinished() {
// TODO Clear search
}
GifsListWidget::GifsListWidget(QWidget *parent) : Inner(parent)
, _section(Section::Gifs) {
resize(st::emojiPanWidth - st::emojiScroll.width - st::buttonRadius, countHeight());
@ -70,8 +77,11 @@ GifsListWidget::GifsListWidget(QWidget *parent) : Inner(parent)
});
}
object_ptr<TWidget> GifsListWidget::createController() {
return object_ptr<Controller>(this);
object_ptr<EmojiPanel::InnerFooter> GifsListWidget::createFooter() {
Expects(_footer == nullptr);
auto result = object_ptr<Footer>(this);
_footer = result;
return std::move(result);
}
void GifsListWidget::setVisibleTopBottom(int visibleTop, int visibleBottom) {
@ -80,6 +90,10 @@ void GifsListWidget::setVisibleTopBottom(int visibleTop, int visibleBottom) {
if (top != getVisibleTop()) {
_lastScrolled = getms();
}
auto visibleHeight = (visibleBottom - visibleTop);
if (visibleBottom + visibleHeight > height()) {
// onInlineRequest(); // TODO
}
}
int GifsListWidget::countHeight() {
@ -252,27 +266,32 @@ void GifsListWidget::clearSelection() {
update();
}
void GifsListWidget::hideFinish(bool completely) {
EmojiPanel::InnerFooter *GifsListWidget::getFooter() const {
return _footer;
}
void GifsListWidget::processHideFinished() {
clearSelection();
if (completely) {
auto itemForget = [](auto &item) {
if (auto document = item->getDocument()) {
document->forget();
}
if (auto photo = item->getPhoto()) {
photo->forget();
}
if (auto result = item->getResult()) {
result->forget();
}
};
clearInlineRows(false);
for_const (auto &item, _gifLayouts) {
itemForget(item.second);
}
void GifsListWidget::processPanelHideFinished() {
auto itemForget = [](auto &item) {
if (auto document = item->getDocument()) {
document->forget();
}
for_const (auto &item, _inlineLayouts) {
itemForget(item.second);
if (auto photo = item->getPhoto()) {
photo->forget();
}
if (auto result = item->getResult()) {
result->forget();
}
};
clearInlineRows(false);
for_const (auto &item, _gifLayouts) {
itemForget(item.second);
}
for_const (auto &item, _inlineLayouts) {
itemForget(item.second);
}
}
@ -503,7 +522,8 @@ int GifsListWidget::refreshInlineRows(UserData *bot, const InlineCacheEntry *ent
}
return false;
};
if (clearResults()) {
auto clearResultsResult = clearResults();
if (clearResultsResult) {
if (resultsDeleted) {
clearInlineRows(true);
deleteUnusedInlineLayouts();

View File

@ -22,6 +22,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "stickers/emoji_panel.h"
namespace Ui {
class RoundButton;
} // namespace Ui
namespace ChatHelpers {
class GifsListWidget : public EmojiPanel::Inner, public InlineBots::Layout::Context, private base::Subscriber {
@ -32,9 +36,8 @@ public:
void refreshRecent() override;
void preloadImages() override;
void hideFinish(bool completely) override;
void clearSelection() override;
object_ptr<TWidget> createController() override;
object_ptr<EmojiPanel::InnerFooter> createFooter() override;
void refreshSavedGifs();
int refreshInlineRows(UserData *bot, const InlineCacheEntry *results, bool resultsDeleted);
@ -58,6 +61,10 @@ protected:
void leaveEventHook(QEvent *e) override;
void leaveToChildEvent(QEvent *e, QWidget *child) override;
void enterFromChildEvent(QEvent *e, QWidget *child) override;
EmojiPanel::InnerFooter *getFooter() const override;
void processHideFinished() override;
void processPanelHideFinished() override;
int countHeight() override;
private slots:
@ -78,7 +85,7 @@ private:
Inlines,
Gifs,
};
class Controller;
class Footer;
void updateSelected();
void paintInlineItems(Painter &p, QRect clip);
@ -120,6 +127,8 @@ private:
int validateExistingInlineRows(const InlineResults &results);
void selectInlineResult(int row, int column);
Footer *_footer = nullptr;
int _selected = -1;
int _pressed = -1;
QPoint _lastMousePos;

View File

@ -153,10 +153,10 @@ emojiPanPadding: 12px;
emojiPanSize: size(45px, 41px);
emojiPanWidth: 345px;
emojiPanMaxHeight: 366px;
emojiPanShowDuration: 2000;
emojiPanDuration: 2000;
emojiPanShowDuration: 200;
emojiPanDuration: 200;
emojiPanHover: windowBgOver;
emojiPanSlideDuration: 2000;
emojiPanSlideDuration: 200;
emojiPanHeader: 42px;
emojiPanHeaderFont: semiboldFont;
@ -173,21 +173,6 @@ emojiSwitchColor: windowActiveTextFg;
emojiSwitchStickers: icon {{ "emoji_switch", emojiSwitchColor }};
emojiSwitchEmoji: icon {{ "emoji_switch-flip_horizontal", emojiSwitchColor }};
stickerPanSize: size(64px, 64px);
stickerPanPadding: 11px;
stickerPanDelete: icon {
{ "emoji_delete_bg", stickerPanDeleteBg },
{ "emoji_delete", stickerPanDeleteFg },
};
stickerPanDeleteOpacity: 0.5;
stickerIconPadding: 5px;
stickerIconOpacity: 0.7;
stickerIconSel: 2px;
stickerIconSelColor: emojiIconFgActive;
stickerIconMove: 400;
stickerPreviewDuration: 150;
stickerPreviewMin: 0.1;
hashtagClose: IconButton {
width: 30px;
height: 30px;
@ -203,6 +188,22 @@ hashtagClose: IconButton {
}
}
stickerPanSize: size(64px, 64px);
stickerPanPadding: 11px;
stickerPanDelete: icon {
{ "emoji_delete_bg", stickerPanDeleteBg },
{ "emoji_delete", stickerPanDeleteFg },
};
stickerPanDeleteOpacity: 0.5;
stickerPanRemoveSet: hashtagClose;
stickerIconPadding: 5px;
stickerIconOpacity: 0.7;
stickerIconSel: 2px;
stickerIconSelColor: emojiIconFgActive;
stickerIconMove: 400;
stickerPreviewDuration: 150;
stickerPreviewMin: 0.1;
stickersToastMaxWidth: 340px;
stickersToastPadding: margins(16px, 13px, 16px, 12px);

File diff suppressed because it is too large Load Diff

View File

@ -21,10 +21,17 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#pragma once
#include "stickers/emoji_panel.h"
#include "core/variant.h"
namespace Ui {
class LinkButton;
} // namespace Ui
namespace ChatHelpers {
class StickersListWidget : public EmojiPanel::Inner, private base::Subscriber {
struct StickerIcon;
class StickersListWidget : public EmojiPanel::Inner, private base::Subscriber, private MTP::Sender {
Q_OBJECT
public:
@ -32,9 +39,8 @@ public:
void refreshRecent() override;
void preloadImages() override;
void hideFinish(bool completely) override;
void clearSelection() override;
object_ptr<TWidget> createController() override;
object_ptr<EmojiPanel::InnerFooter> createFooter() override;
void showStickerSet(uint64 setId);
@ -42,6 +48,7 @@ public:
void refreshRecentStickers(bool resize = true);
void fillIcons(QList<StickerIcon> &icons);
bool preventAutoHide();
void setVisibleTopBottom(int visibleTop, int visibleBottom) override;
@ -60,6 +67,10 @@ protected:
void leaveEventHook(QEvent *e) override;
void leaveToChildEvent(QEvent *e, QWidget *child) override;
void enterFromChildEvent(QEvent *e, QWidget *child) override;
EmojiPanel::InnerFooter *getFooter() const override;
void processHideFinished() override;
void processPanelHideFinished() override;
int countHeight() override;
private slots:
@ -68,23 +79,38 @@ private slots:
signals:
void selected(DocumentData *sticker);
void displaySet(quint64 setId);
void installSet(quint64 setId);
void removeSet(quint64 setId);
void refreshIcons(bool scrollAnimation);
void scrollUpdated();
void checkForHide();
private:
class Footer;
enum class Section {
Featured,
Stickers,
};
class Controller;
static constexpr auto kRefreshIconsScrollAnimation = true;
static constexpr auto kRefreshIconsNoAnimation = false;
struct OverSticker {
int section;
int index;
bool overDelete;
};
struct OverSet {
int section;
};
struct OverButton {
int section;
};
friend inline bool operator==(OverSticker a, OverSticker b) {
return (a.section == b.section) && (a.index == b.index) && (a.overDelete == b.overDelete);
}
friend inline bool operator==(OverSet a, OverSet b) {
return (a.section == b.section);
}
friend inline bool operator==(OverButton a, OverButton b) {
return (a.section == b.section);
}
using OverState = base::optional_variant<OverSticker, OverSet, OverButton>;
struct SectionInfo {
int section = 0;
@ -99,10 +125,22 @@ private:
SectionInfo sectionInfo(int section) const;
SectionInfo sectionInfoByOffset(int yOffset) const;
void updateSelected();
void setSelected(int newSelected, int newSelectedFeaturedSet, int newSelectedFeaturedSetAdd);
void displaySet(quint64 setId);
void installSet(quint64 setId);
void removeSet(quint64 setId);
void setPressedFeaturedSetAdd(int newPressedFeaturedSetAdd);
void updateSelected();
void setSelected(OverState newSelected);
void setPressed(OverState newPressed);
QSharedPointer<Ui::RippleAnimation> createButtonRipple(int section);
QPoint buttonRippleTopLeft(int section) const;
enum class ValidateIconAnimations {
Full,
Scroll,
None,
};
void validateSelectedIcon(ValidateIconAnimations animations);
struct Set {
Set(uint64 id, MTPDstickerSet::Flags flags, const QString &title, int32 hoversSize, const StickerPack &pack = StickerPack()) : id(id), flags(flags), title(title), pack(pack) {
@ -126,9 +164,12 @@ private:
void paintFeaturedStickers(Painter &p, QRect clip);
void paintStickers(Painter &p, QRect clip);
void paintSticker(Painter &p, Set &set, int y, int index, bool selected, bool deleteSelected);
int stickersRight() const;
bool featuredHasAddButton(int index) const;
int featuredContentWidth() const;
QRect featuredAddRect(int y) const;
QRect featuredAddRect(int index) const;
bool hasRemoveButton(int index) const;
QRect removeButtonRect(int index) const;
enum class AppendSkip {
Archived,
@ -140,6 +181,8 @@ private:
int stickersLeft() const;
QRect stickerRect(int section, int sel);
void removeRecentSticker(int section, int index);
Sets _mySets;
Sets _featuredSets;
OrderedSet<uint64> _installedLocallySets;
@ -147,15 +190,14 @@ private:
Section _section = Section::Stickers;
void removeRecentSticker(int section, int index);
uint64 _displayingSetId = 0;
uint64 _removingSetId = 0;
int _selected = -1;
int _pressed = -1;
int _selectedFeaturedSet = -1;
int _pressedFeaturedSet = -1;
int _selectedFeaturedSetAdd = -1;
int _pressedFeaturedSetAdd = -1;
QPoint _lastMousePos;
Footer *_footer = nullptr;
OverState _selected = nullptr;
OverState _pressed = nullptr;
QPoint _lastMousePosition;
QString _addText;
int _addWidth;

View File

@ -15239,23 +15239,32 @@ void Init() {
Items.emplace_back(internal::ComputeId(0xd83c, 0xddff, 0xd83c, 0xddfc), 6, 54, false, false, nullptr, tag);
}
int GetPackCount(DBIEmojiSection tab) {
switch (tab) {
case dbiesPeople: return 291;
case dbiesNature: return 159;
case dbiesFood: return 86;
case dbiesActivity: return 80;
case dbiesTravel: return 119;
case dbiesObjects: return 173;
case dbiesSymbols: return 524;
case dbiesRecent: return GetRecent().size();
int GetSectionCount(Section section) {
switch (section) {
case Section::Recent: return GetRecent().size();
case Section::People: return 291;
case Section::Nature: return 159;
case Section::Food: return 86;
case Section::Activity: return 80;
case Section::Travel: return 119;
case Section::Objects: return 173;
case Section::Symbols: return 524;
}
return 0;
}
EmojiPack GetPack(DBIEmojiSection tab) {
switch (tab) {
case dbiesPeople: {
EmojiPack GetSection(Section section) {
switch (section) {
case Section::Recent: {
auto result = EmojiPack();
result.reserve(GetRecent().size());
for (auto &item : GetRecent()) {
result.push_back(item.first);
}
return result;
} break;
case Section::People: {
static auto result = EmojiPack();
if (result.isEmpty()) {
result.reserve(291);
@ -15554,7 +15563,7 @@ EmojiPack GetPack(DBIEmojiSection tab) {
return result;
} break;
case dbiesNature: {
case Section::Nature: {
static auto result = EmojiPack();
if (result.isEmpty()) {
result.reserve(159);
@ -15721,7 +15730,7 @@ EmojiPack GetPack(DBIEmojiSection tab) {
return result;
} break;
case dbiesFood: {
case Section::Food: {
static auto result = EmojiPack();
if (result.isEmpty()) {
result.reserve(86);
@ -15815,7 +15824,7 @@ EmojiPack GetPack(DBIEmojiSection tab) {
return result;
} break;
case dbiesActivity: {
case Section::Activity: {
static auto result = EmojiPack();
if (result.isEmpty()) {
result.reserve(80);
@ -15903,7 +15912,7 @@ EmojiPack GetPack(DBIEmojiSection tab) {
return result;
} break;
case dbiesTravel: {
case Section::Travel: {
static auto result = EmojiPack();
if (result.isEmpty()) {
result.reserve(119);
@ -16030,7 +16039,7 @@ EmojiPack GetPack(DBIEmojiSection tab) {
return result;
} break;
case dbiesObjects: {
case Section::Objects: {
static auto result = EmojiPack();
if (result.isEmpty()) {
result.reserve(173);
@ -16211,7 +16220,7 @@ EmojiPack GetPack(DBIEmojiSection tab) {
return result;
} break;
case dbiesSymbols: {
case Section::Symbols: {
static auto result = EmojiPack();
if (result.isEmpty()) {
result.reserve(524);
@ -16742,15 +16751,6 @@ EmojiPack GetPack(DBIEmojiSection tab) {
}
return result;
} break;
case dbiesRecent: {
auto result = EmojiPack();
result.reserve(GetRecent().size());
for (auto &item : GetRecent()) {
result.push_back(item.first);
}
return result;
} break;
}
return EmojiPack();
}

View File

@ -24,6 +24,22 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace Ui {
namespace Emoji {
constexpr auto kPostfix = static_cast<ushort>(0xFE0F);
constexpr auto kPanelPerRow = 7;
constexpr auto kPanelRowsPerPage = 6;
enum class Section {
Recent,
People,
Nature,
Food,
Activity,
Travel,
Objects,
Symbols,
};
namespace internal {
EmojiPtr ByIndex(int index);
@ -50,10 +66,6 @@ EmojiPtr FindReplace(const QChar *ch, const QChar *end, int *outLength = nullptr
void Init();
constexpr auto kPostfix = static_cast<ushort>(0xFE0F);
constexpr auto kPanelPerRow = 7;
constexpr auto kPanelRowsPerPage = 6;
class One {
struct CreationTag {
};
@ -221,8 +233,8 @@ inline QString Filename(int index = Index()) {
return QString::fromLatin1(EmojiNames[index]);
}
int GetPackCount(DBIEmojiSection tab);
EmojiPack GetPack(DBIEmojiSection tab);
int GetSectionCount(Section section);
EmojiPack GetSection(Section section);
inline void appendPartToResult(QString &result, const QChar *start, const QChar *from, const QChar *to, EntitiesInText *inOutEntities) {
if (to > from) {