Made TabbedSelector more flexible.

This commit is contained in:
23rd 2021-03-15 13:51:13 +03:00
parent 049945a9b9
commit 7d2b20e624
6 changed files with 318 additions and 129 deletions

View file

@ -1379,6 +1379,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_switch_stickers" = "Stickers"; "lng_switch_stickers" = "Stickers";
"lng_switch_emoji" = "Emoji"; "lng_switch_emoji" = "Emoji";
"lng_switch_gifs" = "GIFs"; "lng_switch_gifs" = "GIFs";
"lng_switch_masks" = "Masks";
"lng_stickers_featured_add" = "Add"; "lng_stickers_featured_add" = "Add";
"lng_gifs_search" = "Search GIFs"; "lng_gifs_search" = "Search GIFs";
"lng_gifs_no_saved" = "You have no saved GIFs yet."; "lng_gifs_no_saved" = "You have no saved GIFs yet.";

View file

@ -604,7 +604,7 @@ void StickersBox::refreshTabs() {
if (!_tabs) return; if (!_tabs) return;
_tabIndices.clear(); _tabIndices.clear();
auto sections = QStringList(); auto sections = std::vector<QString>();
sections.push_back(tr::lng_stickers_installed_tab(tr::now).toUpper()); sections.push_back(tr::lng_stickers_installed_tab(tr::now).toUpper());
_tabIndices.push_back(Section::Installed); _tabIndices.push_back(Section::Installed);
if (!session().data().stickers().featuredSetsOrder().isEmpty()) { if (!session().data().stickers().featuredSetsOrder().isEmpty()) {

View file

@ -256,8 +256,12 @@ void TabbedSelector::SlideAnimation::paintFrame(QPainter &p, float64 dt, float64
p.drawImage(outerLeft / cIntRetinaFactor(), outerTop / cIntRetinaFactor(), _frame, outerLeft, outerTop, outerRight - outerLeft, outerBottom - outerTop); p.drawImage(outerLeft / cIntRetinaFactor(), outerTop / cIntRetinaFactor(), _frame, outerLeft, outerTop, outerRight - outerLeft, outerBottom - outerTop);
} }
TabbedSelector::Tab::Tab(SelectorTab type, object_ptr<Inner> widget) TabbedSelector::Tab::Tab(
SelectorTab type,
int index,
object_ptr<Inner> widget)
: _type(type) : _type(type)
, _index(index)
, _widget(std::move(widget)) , _widget(std::move(widget))
, _weak(_widget) , _weak(_widget)
, _footer(_widget ? _widget->createFooter() : nullptr) { , _footer(_widget ? _widget->createFooter() : nullptr) {
@ -292,33 +296,52 @@ TabbedSelector::TabbedSelector(
, _topShadow(full() ? object_ptr<Ui::PlainShadow>(this) : nullptr) , _topShadow(full() ? object_ptr<Ui::PlainShadow>(this) : nullptr)
, _bottomShadow(this) , _bottomShadow(this)
, _scroll(this, st::emojiScroll) , _scroll(this, st::emojiScroll)
, _tabs { { , _tabs([&] {
createTab(SelectorTab::Emoji), std::vector<Tab> tabs;
createTab(SelectorTab::Stickers), if (full()) {
createTab(SelectorTab::Gifs), tabs.reserve(3);
} } tabs.push_back(createTab(SelectorTab::Emoji, 0));
tabs.push_back(createTab(SelectorTab::Stickers, 1));
tabs.push_back(createTab(SelectorTab::Gifs, 2));
} else if (mediaEditor()) {
tabs.reserve(2);
tabs.push_back(createTab(SelectorTab::Stickers, 0));
tabs.push_back(createTab(SelectorTab::Masks, 1));
} else {
tabs.reserve(1);
tabs.push_back(createTab(SelectorTab::Emoji, 0));
}
return tabs;
}())
, _currentTabType(full() , _currentTabType(full()
? session().settings().selectorTab() ? session().settings().selectorTab()
: SelectorTab::Emoji) { : mediaEditor()
? SelectorTab::Stickers
: SelectorTab::Emoji)
, _hasEmojiTab(ranges::contains(_tabs, SelectorTab::Emoji, &Tab::type))
, _hasStickersTab(ranges::contains(_tabs, SelectorTab::Stickers, &Tab::type))
, _hasGifsTab(ranges::contains(_tabs, SelectorTab::Gifs, &Tab::type))
, _hasMasksTab(ranges::contains(_tabs, SelectorTab::Masks, &Tab::type))
, _tabbed(_tabs.size() > 1) {
resize(st::emojiPanWidth, st::emojiPanMaxHeight); resize(st::emojiPanWidth, st::emojiPanMaxHeight);
for (auto &tab : _tabs) { for (auto &tab : _tabs) {
if (!tab.widget()) {
continue;
}
tab.footer()->hide(); tab.footer()->hide();
tab.widget()->hide(); tab.widget()->hide();
} }
createTabsSlider(); if (tabbed()) {
createTabsSlider();
}
setWidgetToScrollArea(); setWidgetToScrollArea();
_bottomShadow->setGeometry(0, _scroll->y() + _scroll->height() - st::lineWidth, width(), st::lineWidth); _bottomShadow->setGeometry(
0,
_scroll->y() + _scroll->height() - st::lineWidth,
width(),
st::lineWidth);
for (auto &tab : _tabs) { for (auto &tab : _tabs) {
const auto widget = tab.widget(); const auto widget = tab.widget();
if (!widget) {
continue;
}
widget->scrollToRequests( widget->scrollToRequests(
) | rpl::start_with_next([=, tab = &tab](int y) { ) | rpl::start_with_next([=, tab = &tab](int y) {
@ -338,7 +361,7 @@ TabbedSelector::TabbedSelector(
} }
rpl::merge( rpl::merge(
(full() (hasStickersTab()
? stickers()->scrollUpdated() | rpl::map_to(0) ? stickers()->scrollUpdated() | rpl::map_to(0)
: rpl::never<int>() | rpl::type_erased()), : rpl::never<int>() | rpl::type_erased()),
_scroll->scrollTopChanges() _scroll->scrollTopChanges()
@ -346,13 +369,15 @@ TabbedSelector::TabbedSelector(
handleScroll(); handleScroll();
}, lifetime()); }, lifetime());
if (full()) { if (_topShadow) {
_topShadow->raise(); _topShadow->raise();
} }
_bottomShadow->raise(); _bottomShadow->raise();
if (full()) { if (_tabsSlider) {
_tabsSlider->raise(); _tabsSlider->raise();
}
if (hasStickersTab() || hasGifsTab()) {
session().changes().peerUpdates( session().changes().peerUpdates(
Data::PeerUpdate::Flag::Rights Data::PeerUpdate::Flag::Rights
) | rpl::filter([=](const Data::PeerUpdate &update) { ) | rpl::filter([=](const Data::PeerUpdate &update) {
@ -360,11 +385,12 @@ TabbedSelector::TabbedSelector(
}) | rpl::start_with_next([=] { }) | rpl::start_with_next([=] {
checkRestrictedPeer(); checkRestrictedPeer();
}, lifetime()); }, lifetime());
}
if (hasStickersTab()) {
session().api().stickerSetInstalled( session().api().stickerSetInstalled(
) | rpl::start_with_next([this](uint64 setId) { ) | rpl::start_with_next([this](uint64 setId) {
_tabsSlider->setActiveSection( _tabsSlider->setActiveSection(indexByType(SelectorTab::Stickers));
static_cast<int>(SelectorTab::Stickers));
stickers()->showStickerSet(setId); stickers()->showStickerSet(setId);
_showRequests.fire({}); _showRequests.fire({});
}, lifetime()); }, lifetime());
@ -387,11 +413,8 @@ Main::Session &TabbedSelector::session() const {
return _controller->session(); return _controller->session();
} }
TabbedSelector::Tab TabbedSelector::createTab(SelectorTab type) { TabbedSelector::Tab TabbedSelector::createTab(SelectorTab type, int index) {
auto createWidget = [&]() -> object_ptr<Inner> { auto createWidget = [&]() -> object_ptr<Inner> {
if (!full() && type != SelectorTab::Emoji) {
return { nullptr };
}
switch (type) { switch (type) {
case SelectorTab::Emoji: case SelectorTab::Emoji:
return object_ptr<EmojiListWidget>(this, _controller); return object_ptr<EmojiListWidget>(this, _controller);
@ -399,42 +422,74 @@ TabbedSelector::Tab TabbedSelector::createTab(SelectorTab type) {
return object_ptr<StickersListWidget>(this, _controller); return object_ptr<StickersListWidget>(this, _controller);
case SelectorTab::Gifs: case SelectorTab::Gifs:
return object_ptr<GifsListWidget>(this, _controller); return object_ptr<GifsListWidget>(this, _controller);
case SelectorTab::Masks:
return object_ptr<StickersListWidget>(this, _controller);
} }
Unexpected("Type in TabbedSelector::createTab."); Unexpected("Type in TabbedSelector::createTab.");
}; };
return Tab{ type, createWidget() }; return Tab{ type, index, createWidget() };
} }
bool TabbedSelector::full() const { bool TabbedSelector::full() const {
return (_mode == Mode::Full); return (_mode == Mode::Full);
} }
bool TabbedSelector::mediaEditor() const {
return (_mode == Mode::MediaEditor);
}
bool TabbedSelector::tabbed() const {
return _tabbed;
}
bool TabbedSelector::hasEmojiTab() const {
return _hasEmojiTab;
}
bool TabbedSelector::hasStickersTab() const {
return _hasStickersTab;
}
bool TabbedSelector::hasGifsTab() const {
return _hasGifsTab;
}
bool TabbedSelector::hasMasksTab() const {
return _hasMasksTab;
}
rpl::producer<EmojiPtr> TabbedSelector::emojiChosen() const { rpl::producer<EmojiPtr> TabbedSelector::emojiChosen() const {
return emoji()->chosen(); return emoji()->chosen();
} }
rpl::producer<TabbedSelector::FileChosen> TabbedSelector::fileChosen() const { rpl::producer<TabbedSelector::FileChosen> TabbedSelector::fileChosen() const {
return full() auto never = rpl::never<TabbedSelector::FileChosen>(
? rpl::merge(stickers()->chosen(), gifs()->fileChosen()) ) | rpl::type_erased();
: rpl::never<TabbedSelector::FileChosen>() | rpl::type_erased(); return rpl::merge(
hasStickersTab() ? stickers()->chosen() : never,
hasGifsTab() ? gifs()->fileChosen() : never,
hasMasksTab() ? masks()->chosen() : never);
} }
auto TabbedSelector::photoChosen() const auto TabbedSelector::photoChosen() const
-> rpl::producer<TabbedSelector::PhotoChosen>{ -> rpl::producer<TabbedSelector::PhotoChosen>{
return full() ? gifs()->photoChosen() : nullptr; return hasGifsTab() ? gifs()->photoChosen() : nullptr;
} }
auto TabbedSelector::inlineResultChosen() const auto TabbedSelector::inlineResultChosen() const
-> rpl::producer<InlineChosen> { -> rpl::producer<InlineChosen> {
return full() ? gifs()->inlineResultChosen() : nullptr; return hasGifsTab() ? gifs()->inlineResultChosen() : nullptr;
} }
rpl::producer<> TabbedSelector::cancelled() const { rpl::producer<> TabbedSelector::cancelled() const {
return full() ? gifs()->cancelRequests() : nullptr; return hasGifsTab() ? gifs()->cancelRequests() : nullptr;
} }
rpl::producer<> TabbedSelector::checkForHide() const { rpl::producer<> TabbedSelector::checkForHide() const {
return full() ? stickers()->checkForHide() : nullptr; auto never = rpl::never<>();
return rpl::merge(
hasStickersTab() ? stickers()->checkForHide() : never,
hasMasksTab() ? masks()->checkForHide() : never);
} }
rpl::producer<> TabbedSelector::slideFinished() const { rpl::producer<> TabbedSelector::slideFinished() const {
@ -442,9 +497,11 @@ rpl::producer<> TabbedSelector::slideFinished() const {
} }
void TabbedSelector::resizeEvent(QResizeEvent *e) { void TabbedSelector::resizeEvent(QResizeEvent *e) {
if (full()) { if (_tabsSlider) {
_tabsSlider->resizeToWidth(width()); _tabsSlider->resizeToWidth(width());
_tabsSlider->moveToLeft(0, 0); _tabsSlider->moveToLeft(0, 0);
}
if (_topShadow && _tabsSlider) {
_topShadow->setGeometry( _topShadow->setGeometry(
_tabsSlider->x(), _tabsSlider->x(),
_tabsSlider->bottomNoMargins() - st::lineWidth, _tabsSlider->bottomNoMargins() - st::lineWidth,
@ -476,14 +533,15 @@ void TabbedSelector::resizeEvent(QResizeEvent *e) {
updateInnerGeometry(); updateInnerGeometry();
updateScrollGeometry(); updateScrollGeometry();
} }
_bottomShadow->setGeometry(0, _scroll->y() + _scroll->height() - st::lineWidth, width(), st::lineWidth); _bottomShadow->setGeometry(
0,
_scroll->y() + _scroll->height() - st::lineWidth,
width(),
st::lineWidth);
updateRestrictedLabelGeometry(); updateRestrictedLabelGeometry();
_footerTop = height() - st::emojiFooterHeight; _footerTop = height() - st::emojiFooterHeight;
for (auto &tab : _tabs) { for (auto &tab : _tabs) {
if (!tab.widget()) {
continue;
}
tab.footer()->resizeToWidth(width()); tab.footer()->resizeToWidth(width());
tab.footer()->moveToLeft(0, _footerTop); tab.footer()->moveToLeft(0, _footerTop);
} }
@ -521,14 +579,22 @@ void TabbedSelector::paintEvent(QPaintEvent *e) {
void TabbedSelector::paintSlideFrame(Painter &p) { void TabbedSelector::paintSlideFrame(Painter &p) {
if (_roundRadius > 0) { if (_roundRadius > 0) {
if (full()) { const auto topPart = QRect(
auto topPart = QRect(0, 0, width(), _tabsSlider->height() + _roundRadius); 0,
Ui::FillRoundRect(p, topPart, st::emojiPanBg, ImageRoundRadius::Small, RectPart::FullTop | RectPart::NoTopBottom); 0,
} else { width(),
auto topPart = QRect(0, 0, width(), 3 * _roundRadius); _tabsSlider
Ui::FillRoundRect(p, topPart, st::emojiPanBg, ImageRoundRadius::Small, RectPart::FullTop); ? _tabsSlider->height() + _roundRadius
} : 3 * _roundRadius);
} else if (full()) { Ui::FillRoundRect(
p,
topPart,
st::emojiPanBg,
ImageRoundRadius::Small,
tabbed()
? RectPart::FullTop | RectPart::NoTopBottom
: RectPart::FullTop);
} else if (_tabsSlider) {
p.fillRect(0, 0, width(), _tabsSlider->height(), st::emojiPanBg); p.fillRect(0, 0, width(), _tabsSlider->height(), st::emojiPanBg);
} }
auto slideDt = _a_slide.value(1.); auto slideDt = _a_slide.value(1.);
@ -536,21 +602,39 @@ void TabbedSelector::paintSlideFrame(Painter &p) {
} }
void TabbedSelector::paintContent(Painter &p) { void TabbedSelector::paintContent(Painter &p) {
auto &bottomBg = hasSectionIcons() ? st::emojiPanCategories : st::emojiPanBg; auto &bottomBg = hasSectionIcons()
? st::emojiPanCategories
: st::emojiPanBg;
if (_roundRadius > 0) { if (_roundRadius > 0) {
if (full()) { const auto topPart = QRect(
auto topPart = QRect(0, 0, width(), _tabsSlider->height() + _roundRadius); 0,
Ui::FillRoundRect(p, topPart, st::emojiPanBg, ImageRoundRadius::Small, RectPart::FullTop | RectPart::NoTopBottom); 0,
} else { width(),
auto topPart = QRect(0, 0, width(), 3 * _roundRadius); _tabsSlider
Ui::FillRoundRect(p, topPart, st::emojiPanBg, ImageRoundRadius::Small, RectPart::FullTop); ? _tabsSlider->height() + _roundRadius
} : 3 * _roundRadius);
Ui::FillRoundRect(
p,
topPart,
st::emojiPanBg,
ImageRoundRadius::Small,
tabbed()
? RectPart::FullTop | RectPart::NoTopBottom
: RectPart::FullTop);
auto bottomPart = QRect(0, _footerTop - _roundRadius, width(), st::emojiFooterHeight + _roundRadius); const auto bottomPart = QRect(
auto bottomParts = RectPart::NoTopBottom | RectPart::FullBottom; 0,
Ui::FillRoundRect(p, bottomPart, bottomBg, ImageRoundRadius::Small, bottomParts); _footerTop - _roundRadius,
width(),
st::emojiFooterHeight + _roundRadius);
Ui::FillRoundRect(
p,
bottomPart,
bottomBg,
ImageRoundRadius::Small,
RectPart::NoTopBottom | RectPart::FullBottom);
} else { } else {
if (full()) { if (_tabsSlider) {
p.fillRect(0, 0, width(), _tabsSlider->height(), st::emojiPanBg); p.fillRect(0, 0, width(), _tabsSlider->height(), st::emojiPanBg);
} }
p.fillRect(0, _footerTop, width(), st::emojiFooterHeight, bottomBg); p.fillRect(0, _footerTop, width(), st::emojiFooterHeight, bottomBg);
@ -561,17 +645,27 @@ void TabbedSelector::paintContent(Painter &p) {
if (_restrictedLabel) { if (_restrictedLabel) {
p.fillRect(0, sidesTop, width(), sidesHeight, st::emojiPanBg); p.fillRect(0, sidesTop, width(), sidesHeight, st::emojiPanBg);
} else { } else {
p.fillRect(myrtlrect(width() - st::emojiScroll.width, sidesTop, st::emojiScroll.width, sidesHeight), st::emojiPanBg); p.fillRect(
p.fillRect(myrtlrect(0, sidesTop, st::roundRadiusSmall, sidesHeight), st::emojiPanBg); myrtlrect(
width() - st::emojiScroll.width,
sidesTop,
st::emojiScroll.width,
sidesHeight),
st::emojiPanBg);
p.fillRect(
myrtlrect(0, sidesTop, st::roundRadiusSmall, sidesHeight),
st::emojiPanBg);
} }
} }
int TabbedSelector::marginTop() const { int TabbedSelector::marginTop() const {
return full() ? (_tabsSlider->height() - st::lineWidth) : _roundRadius; return _tabsSlider
? (_tabsSlider->height() - st::lineWidth)
: _roundRadius;
} }
int TabbedSelector::scrollTop() const { int TabbedSelector::scrollTop() const {
return full() ? marginTop() : 0; return tabbed() ? marginTop() : 0;
} }
int TabbedSelector::marginBottom() const { int TabbedSelector::marginBottom() const {
@ -579,7 +673,7 @@ int TabbedSelector::marginBottom() const {
} }
void TabbedSelector::refreshStickers() { void TabbedSelector::refreshStickers() {
if (!full()) { if (!hasStickersTab()) {
return; return;
} }
stickers()->refreshStickers(); stickers()->refreshStickers();
@ -589,9 +683,9 @@ void TabbedSelector::refreshStickers() {
} }
bool TabbedSelector::preventAutoHide() const { bool TabbedSelector::preventAutoHide() const {
return full() return (hasStickersTab() ? stickers()->preventAutoHide() : false)
? (stickers()->preventAutoHide() || hasMenu()) || (hasMasksTab() ? masks()->preventAutoHide() : false)
: false; || hasMenu();
} }
bool TabbedSelector::hasMenu() const { bool TabbedSelector::hasMenu() const {
@ -603,13 +697,17 @@ QImage TabbedSelector::grabForAnimation() {
auto slideAnimation = base::take(_a_slide); auto slideAnimation = base::take(_a_slide);
showAll(); showAll();
if (full()) { if (_topShadow) {
_topShadow->hide(); _topShadow->hide();
}
if (_tabsSlider) {
_tabsSlider->hide(); _tabsSlider->hide();
} }
Ui::SendPendingMoveResizeEvents(this); Ui::SendPendingMoveResizeEvents(this);
auto result = QImage(size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); auto result = QImage(
size() * cIntRetinaFactor(),
QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(cRetinaFactor()); result.setDevicePixelRatio(cRetinaFactor());
result.fill(Qt::transparent); result.fill(Qt::transparent);
render(&result); render(&result);
@ -630,9 +728,6 @@ QRect TabbedSelector::floatPlayerAvailableRect() const {
void TabbedSelector::hideFinished() { void TabbedSelector::hideFinished() {
for (auto &tab : _tabs) { for (auto &tab : _tabs) {
if (!tab.widget()) {
continue;
}
tab.widget()->panelHideFinished(); tab.widget()->panelHideFinished();
} }
_a_slide.stop(); _a_slide.stop();
@ -640,7 +735,7 @@ void TabbedSelector::hideFinished() {
} }
void TabbedSelector::showStarted() { void TabbedSelector::showStarted() {
if (full()) { if (hasStickersTab()) {
session().api().updateStickers(); session().api().updateStickers();
} }
currentTab()->widget()->refreshRecent(); currentTab()->widget()->refreshRecent();
@ -670,13 +765,14 @@ void TabbedSelector::afterShown() {
} }
void TabbedSelector::setCurrentPeer(PeerData *peer) { void TabbedSelector::setCurrentPeer(PeerData *peer) {
if (!full()) { if (hasGifsTab()) {
return; gifs()->setInlineQueryPeer(peer);
} }
gifs()->setInlineQueryPeer(peer);
_currentPeer = peer; _currentPeer = peer;
checkRestrictedPeer(); checkRestrictedPeer();
stickers()->showMegagroupSet(peer ? peer->asMegagroup() : nullptr); if (hasStickersTab()) {
stickers()->showMegagroupSet(peer ? peer->asMegagroup() : nullptr);
}
} }
void TabbedSelector::checkRestrictedPeer() { void TabbedSelector::checkRestrictedPeer() {
@ -730,16 +826,20 @@ void TabbedSelector::showAll() {
_scroll->show(); _scroll->show();
_bottomShadow->setVisible(_currentTabType == SelectorTab::Gifs); _bottomShadow->setVisible(_currentTabType == SelectorTab::Gifs);
} }
if (full()) { if (_topShadow) {
_topShadow->show(); _topShadow->show();
}
if (_tabsSlider) {
_tabsSlider->show(); _tabsSlider->show();
} }
} }
void TabbedSelector::hideForSliding() { void TabbedSelector::hideForSliding() {
hideChildren(); hideChildren();
if (full()) { if (_topShadow) {
_topShadow->show(); _topShadow->show();
}
if (_tabsSlider) {
_tabsSlider->show(); _tabsSlider->show();
} }
currentTab()->widget()->clearSelection(); currentTab()->widget()->clearSelection();
@ -753,25 +853,33 @@ void TabbedSelector::handleScroll() {
void TabbedSelector::setRoundRadius(int radius) { void TabbedSelector::setRoundRadius(int radius) {
_roundRadius = radius; _roundRadius = radius;
if (full()) { if (_tabsSlider) {
_tabsSlider->setRippleTopRoundRadius(_roundRadius); _tabsSlider->setRippleTopRoundRadius(_roundRadius);
} }
} }
void TabbedSelector::createTabsSlider() { void TabbedSelector::createTabsSlider() {
if (!full()) {
return;
}
_tabsSlider.create(this, st::emojiTabs); _tabsSlider.create(this, st::emojiTabs);
auto sections = QStringList(); const auto sections = ranges::views::all(
sections.push_back(tr::lng_switch_emoji(tr::now).toUpper()); _tabs
sections.push_back(tr::lng_switch_stickers(tr::now).toUpper()); ) | ranges::views::transform([=](const Tab &tab) {
sections.push_back(tr::lng_switch_gifs(tr::now).toUpper()); return [type = tab.type()] {
switch (type) {
case SelectorTab::Emoji:
return tr::lng_switch_emoji;
case SelectorTab::Stickers:
return tr::lng_switch_stickers;
case SelectorTab::Gifs:
return tr::lng_switch_gifs;
case SelectorTab::Masks:
return tr::lng_switch_masks;
}
}()(tr::now).toUpper();
}) | ranges::to_vector;
_tabsSlider->setSections(sections); _tabsSlider->setSections(sections);
_tabsSlider->setActiveSectionFast(static_cast<int>(_currentTabType)); _tabsSlider->setActiveSectionFast(indexByType(_currentTabType));
_tabsSlider->sectionActivated( _tabsSlider->sectionActivated(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
switchTab(); switchTab();
@ -783,18 +891,19 @@ bool TabbedSelector::hasSectionIcons() const {
} }
void TabbedSelector::switchTab() { void TabbedSelector::switchTab() {
Expects(full()); Expects(tabbed());
auto tab = _tabsSlider->activeSection(); const auto tab = _tabsSlider->activeSection();
Assert(tab >= 0 && tab < Tab::kCount); Assert(tab >= 0 && tab < _tabs.size());
auto newTabType = static_cast<SelectorTab>(tab); const auto newTabType = typeByIndex(tab);
if (_currentTabType == newTabType) { if (_currentTabType == newTabType) {
_scroll->scrollToY(0); _scroll->scrollToY(0);
return; return;
} }
auto wasSectionIcons = hasSectionIcons(); const auto wasSectionIcons = hasSectionIcons();
auto wasTab = _currentTabType; const auto wasTab = _currentTabType;
const auto wasIndex = indexByType(_currentTabType);
currentTab()->saveScrollTop(); currentTab()->saveScrollTop();
beforeHiding(); beforeHiding();
@ -817,21 +926,38 @@ void TabbedSelector::switchTab() {
auto nowCache = grabForAnimation(); auto nowCache = grabForAnimation();
auto direction = (wasTab > _currentTabType) ? SlideAnimation::Direction::LeftToRight : SlideAnimation::Direction::RightToLeft; auto direction = (wasIndex > indexByType(_currentTabType))
? SlideAnimation::Direction::LeftToRight
: SlideAnimation::Direction::RightToLeft;
if (direction == SlideAnimation::Direction::LeftToRight) { if (direction == SlideAnimation::Direction::LeftToRight) {
std::swap(wasCache, nowCache); std::swap(wasCache, nowCache);
} }
_slideAnimation = std::make_unique<SlideAnimation>(); _slideAnimation = std::make_unique<SlideAnimation>();
auto slidingRect = QRect(0, _scroll->y() * cIntRetinaFactor(), width() * cIntRetinaFactor(), (height() - _scroll->y()) * cIntRetinaFactor()); const auto slidingRect = QRect(
_slideAnimation->setFinalImages(direction, std::move(wasCache), std::move(nowCache), slidingRect, wasSectionIcons); 0,
_slideAnimation->setCornerMasks(Images::CornersMask(ImageRoundRadius::Small)); _scroll->y() * cIntRetinaFactor(),
width() * cIntRetinaFactor(),
(height() - _scroll->y()) * cIntRetinaFactor());
_slideAnimation->setFinalImages(
direction,
std::move(wasCache),
std::move(nowCache),
slidingRect,
wasSectionIcons);
_slideAnimation->setCornerMasks(
Images::CornersMask(ImageRoundRadius::Small));
_slideAnimation->start(); _slideAnimation->start();
hideForSliding(); hideForSliding();
getTab(wasTab)->widget()->hideFinished(); getTab(wasIndex)->widget()->hideFinished();
_a_slide.start([this] { update(); }, 0., 1., st::emojiPanSlideDuration, anim::linear); _a_slide.start(
[=] { update(); },
0.,
1.,
st::emojiPanSlideDuration,
anim::linear);
update(); update();
if (full()) { if (full()) {
@ -841,19 +967,31 @@ void TabbedSelector::switchTab() {
} }
not_null<EmojiListWidget*> TabbedSelector::emoji() const { not_null<EmojiListWidget*> TabbedSelector::emoji() const {
return static_cast<EmojiListWidget*>(getTab(SelectorTab::Emoji)->widget()); Expects(hasEmojiTab());
return static_cast<EmojiListWidget*>(
getTab(indexByType(SelectorTab::Emoji))->widget());
} }
not_null<StickersListWidget*> TabbedSelector::stickers() const { not_null<StickersListWidget*> TabbedSelector::stickers() const {
Expects(full()); Expects(hasStickersTab());
return static_cast<StickersListWidget*>(getTab(SelectorTab::Stickers)->widget()); return static_cast<StickersListWidget*>(
getTab(indexByType(SelectorTab::Stickers))->widget());
} }
not_null<GifsListWidget*> TabbedSelector::gifs() const { not_null<GifsListWidget*> TabbedSelector::gifs() const {
Expects(full()); Expects(hasGifsTab());
return static_cast<GifsListWidget*>(getTab(SelectorTab::Gifs)->widget()); return static_cast<GifsListWidget*>(
getTab(indexByType(SelectorTab::Gifs))->widget());
}
not_null<StickersListWidget*> TabbedSelector::masks() const {
Expects(hasMasksTab());
return static_cast<StickersListWidget*>(
getTab(indexByType(SelectorTab::Masks))->widget());
} }
void TabbedSelector::setWidgetToScrollArea() { void TabbedSelector::setWidgetToScrollArea() {
@ -873,7 +1011,7 @@ void TabbedSelector::scrollToY(int y) {
_scroll->scrollToY(y); _scroll->scrollToY(y);
// Qt render glitch workaround, shadow sometimes disappears if we just scroll to y. // Qt render glitch workaround, shadow sometimes disappears if we just scroll to y.
if (full()) { if (_topShadow) {
_topShadow->update(); _topShadow->update();
} }
} }
@ -894,6 +1032,40 @@ rpl::producer<> TabbedSelector::contextMenuRequested() const {
}) | rpl::to_empty; }) | rpl::to_empty;
} }
SelectorTab TabbedSelector::typeByIndex(int index) const {
for (const auto &tab : _tabs) {
if (tab.index() == index) {
return tab.type();
}
}
Unexpected("Type in TabbedSelector::typeByIndex.");
}
int TabbedSelector::indexByType(SelectorTab type) const {
for (const auto &tab : _tabs) {
if (tab.type() == type) {
return tab.index();
}
}
Unexpected("Index in TabbedSelector::indexByType.");
}
not_null<TabbedSelector::Tab*> TabbedSelector::getTab(int index) {
return &(_tabs[index]);
}
not_null<const TabbedSelector::Tab*> TabbedSelector::getTab(int index) const {
return &_tabs[index];
}
not_null<TabbedSelector::Tab*> TabbedSelector::currentTab() {
return &_tabs[indexByType(_currentTabType)];
}
not_null<const TabbedSelector::Tab*> TabbedSelector::currentTab() const {
return &_tabs[indexByType(_currentTabType)];
}
TabbedSelector::Inner::Inner( TabbedSelector::Inner::Inner(
QWidget *parent, QWidget *parent,
not_null<Window::SessionController*> controller) not_null<Window::SessionController*> controller)
@ -917,7 +1089,9 @@ void TabbedSelector::Inner::disableScroll(bool disabled) {
_disableScrollRequests.fire_copy(disabled); _disableScrollRequests.fire_copy(disabled);
} }
void TabbedSelector::Inner::visibleTopBottomUpdated(int visibleTop, int visibleBottom) { void TabbedSelector::Inner::visibleTopBottomUpdated(
int visibleTop,
int visibleBottom) {
_visibleTop = visibleTop; _visibleTop = visibleTop;
_visibleBottom = visibleBottom; _visibleBottom = visibleBottom;
} }

View file

@ -44,6 +44,7 @@ enum class SelectorTab {
Emoji, Emoji,
Stickers, Stickers,
Gifs, Gifs,
Masks,
}; };
class EmojiListWidget; class EmojiListWidget;
@ -63,7 +64,8 @@ public:
using InlineChosen = InlineBots::ResultSelected; using InlineChosen = InlineBots::ResultSelected;
enum class Mode { enum class Mode {
Full, Full,
EmojiOnly EmojiOnly,
MediaEditor,
}; };
TabbedSelector( TabbedSelector(
@ -130,9 +132,7 @@ protected:
private: private:
class Tab { class Tab {
public: public:
static constexpr auto kCount = 3; Tab(SelectorTab type, int index, object_ptr<Inner> widget);
Tab(SelectorTab type, object_ptr<Inner> widget);
object_ptr<Inner> takeWidget(); object_ptr<Inner> takeWidget();
void returnWidget(object_ptr<Inner> widget); void returnWidget(object_ptr<Inner> widget);
@ -140,6 +140,9 @@ private:
SelectorTab type() const { SelectorTab type() const {
return _type; return _type;
} }
int index() const {
return _index;
}
Inner *widget() const { Inner *widget() const {
return _weak; return _weak;
} }
@ -156,7 +159,8 @@ private:
} }
private: private:
SelectorTab _type = SelectorTab::Emoji; const SelectorTab _type;
const int _index;
object_ptr<Inner> _widget = { nullptr }; object_ptr<Inner> _widget = { nullptr };
QPointer<Inner> _weak; QPointer<Inner> _weak;
object_ptr<InnerFooter> _footer; object_ptr<InnerFooter> _footer;
@ -165,7 +169,13 @@ private:
}; };
bool full() const; bool full() const;
Tab createTab(SelectorTab type); bool mediaEditor() const;
bool tabbed() const;
bool hasEmojiTab() const;
bool hasStickersTab() const;
bool hasGifsTab() const;
bool hasMasksTab() const;
Tab createTab(SelectorTab type, int index);
void paintSlideFrame(Painter &p); void paintSlideFrame(Painter &p);
void paintContent(Painter &p); void paintContent(Painter &p);
@ -182,25 +192,23 @@ private:
void showAll(); void showAll();
void hideForSliding(); void hideForSliding();
SelectorTab typeByIndex(int index) const;
int indexByType(SelectorTab type) const;
bool hasSectionIcons() const; bool hasSectionIcons() const;
void setWidgetToScrollArea(); void setWidgetToScrollArea();
void createTabsSlider(); void createTabsSlider();
void switchTab(); void switchTab();
not_null<Tab*> getTab(SelectorTab type) {
return &_tabs[static_cast<int>(type)]; not_null<Tab*> getTab(int index);
} not_null<const Tab*> getTab(int index) const;
not_null<const Tab*> getTab(SelectorTab type) const { not_null<Tab*> currentTab();
return &_tabs[static_cast<int>(type)]; not_null<const Tab*> currentTab() const;
}
not_null<Tab*> currentTab() {
return getTab(_currentTabType);
}
not_null<const Tab*> currentTab() const {
return getTab(_currentTabType);
}
not_null<EmojiListWidget*> emoji() const; not_null<EmojiListWidget*> emoji() const;
not_null<StickersListWidget*> stickers() const; not_null<StickersListWidget*> stickers() const;
not_null<GifsListWidget*> gifs() const; not_null<GifsListWidget*> gifs() const;
not_null<StickersListWidget*> masks() const;
const not_null<Window::SessionController*> _controller; const not_null<Window::SessionController*> _controller;
@ -218,9 +226,15 @@ private:
object_ptr<Ui::PlainShadow> _bottomShadow; object_ptr<Ui::PlainShadow> _bottomShadow;
object_ptr<Ui::ScrollArea> _scroll; object_ptr<Ui::ScrollArea> _scroll;
object_ptr<Ui::FlatLabel> _restrictedLabel = { nullptr }; object_ptr<Ui::FlatLabel> _restrictedLabel = { nullptr };
std::array<Tab, Tab::kCount> _tabs; std::vector<Tab> _tabs;
SelectorTab _currentTabType = SelectorTab::Emoji; SelectorTab _currentTabType = SelectorTab::Emoji;
const bool _hasEmojiTab;
const bool _hasStickersTab;
const bool _hasGifsTab;
const bool _hasMasksTab;
const bool _tabbed;
base::unique_qptr<Ui::PopupMenu> _menu; base::unique_qptr<Ui::PopupMenu> _menu;
Fn<void(SelectorTab)> _afterShownCallback; Fn<void(SelectorTab)> _afterShownCallback;

View file

@ -62,8 +62,8 @@ void DiscreteSlider::addSection(const QString &label) {
resizeToWidth(width()); resizeToWidth(width());
} }
void DiscreteSlider::setSections(const QStringList &labels) { void DiscreteSlider::setSections(const std::vector<QString> &labels) {
Assert(!labels.isEmpty()); Assert(!labels.empty());
_sections.clear(); _sections.clear();
for (const auto &label : labels) { for (const auto &label : labels) {

View file

@ -20,7 +20,7 @@ public:
DiscreteSlider(QWidget *parent); DiscreteSlider(QWidget *parent);
void addSection(const QString &label); void addSection(const QString &label);
void setSections(const QStringList &labels); void setSections(const std::vector<QString> &labels);
int activeSection() const { int activeSection() const {
return _activeIndex; return _activeIndex;
} }