diff --git a/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.cpp b/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.cpp index 9e109e3e9..ab5a2089e 100644 --- a/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.cpp +++ b/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/chat_filters_tabs_slider.h" #include "ui/effects/ripple_animation.h" +#include "ui/widgets/side_bar_button.h" #include "styles/style_widgets.h" #include @@ -57,6 +58,7 @@ int ChatsFiltersTabs::centerOfSection(int section) const { void ChatsFiltersTabs::fitWidthToSections() { const auto widths = countSectionsWidths(0); resizeToWidth(ranges::accumulate(widths, .0)); + _lockedFromX = calculateLockedFromX(); } void ChatsFiltersTabs::setUnreadCount(int index, int unreadCount) { @@ -90,6 +92,37 @@ void ChatsFiltersTabs::setUnreadCount(int index, int unreadCount) { } } +int ChatsFiltersTabs::calculateLockedFromX() const { + if (!_lockedFrom) { + return std::numeric_limits::max(); + } + auto left = 0; + auto index = 0; + enumerateSections([&](const Section §ion) { + const auto currentRight = section.left + section.width; + if (index == _lockedFrom) { + return false; + } + left = currentRight; + index++; + return true; + }); + return left ? left : std::numeric_limits::max(); +} + +void ChatsFiltersTabs::setLockedFrom(int index) { + _lockedFrom = index; + _lockedFromX = calculateLockedFromX(); + if (!index) { + _paletteLifetime.destroy(); + return; + } + _paletteLifetime = style::PaletteChanged( + ) | rpl::start_with_next([this] { + _lockCache.emplace(Ui::SideBarLockIcon(_st.labelFg)); + }); +} + QImage ChatsFiltersTabs::cacheUnreadCount(int count) const { const auto widthIndex = (count < 10) ? 0 : (count < 100) ? 1 : 2; auto image = QImage( @@ -144,6 +177,11 @@ void ChatsFiltersTabs::paintEvent(QPaintEvent *e) { section.contentWidth, _st.labelStyle.font->height); if (rect.intersects(clip)) { + const auto locked = (_lockedFrom && (index >= _lockedFrom)); + if (locked) { + constexpr auto kPremiumLockedOpacity = 0.6; + p.setOpacity(kPremiumLockedOpacity); + } p.setPen(anim::pen(_st.labelFg, _st.labelFgActive, active)); section.label.draw(p, { .position = QPoint(labelLeft, _st.labelTop), @@ -161,6 +199,18 @@ void ChatsFiltersTabs::paintEvent(QPaintEvent *e) { it->second.cache); } } + if (locked) { + if (!_lockCache) { + _lockCache.emplace(Ui::SideBarLockIcon(_st.labelFg)); + } + const auto size = _lockCache->size() + / style::DevicePixelRatio(); + p.drawImage( + labelLeft + (section.label.maxWidth() - size.width()) / 2, + height() - size.height() - st::lineWidth, + *_lockCache); + p.setOpacity(1.0); + } } index++; return true; @@ -188,14 +238,36 @@ void ChatsFiltersTabs::paintEvent(QPaintEvent *e) { void ChatsFiltersTabs::mousePressEvent(QMouseEvent *e) { const auto mouseButton = e->button(); if (mouseButton == Qt::MouseButton::LeftButton) { - Ui::SettingsSlider::mousePressEvent(e); + _lockedPressed = (e->pos().x() >= _lockedFromX); + if (_lockedPressed) { + Ui::RpWidget::mousePressEvent(e); + } else { + Ui::SettingsSlider::mousePressEvent(e); + } } else { Ui::RpWidget::mousePressEvent(e); } } +void ChatsFiltersTabs::mouseReleaseEvent(QMouseEvent *e) { + const auto mouseButton = e->button(); + if (mouseButton == Qt::MouseButton::LeftButton) { + if (base::take(_lockedPressed)) { + _lockedPressed = false; + _lockedClicked.fire({}); + } else { + Ui::SettingsSlider::mouseReleaseEvent(e); + } + } else { + Ui::RpWidget::mouseReleaseEvent(e); + } +} + void ChatsFiltersTabs::contextMenuEvent(QContextMenuEvent *e) { const auto pos = e->pos(); + if (pos.x() >= _lockedFromX) { + return; + } auto left = 0; auto index = 0; enumerateSections([&](const Section §ion) { @@ -214,4 +286,8 @@ rpl::producer ChatsFiltersTabs::contextMenuRequested() const { return _contextMenuRequested.events(); } +rpl::producer<> ChatsFiltersTabs::lockedClicked() const { + return _lockedClicked.events(); +} + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.h b/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.h index 786368581..ca311c7fe 100644 --- a/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.h +++ b/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.h @@ -28,16 +28,20 @@ public: [[nodiscard]] int centerOfSection(int section) const; void fitWidthToSections(); void setUnreadCount(int index, int unreadCount); + void setLockedFrom(int index); [[nodiscard]] rpl::producer contextMenuRequested() const; + [[nodiscard]] rpl::producer<> lockedClicked() const; protected: void paintEvent(QPaintEvent *e) override; void mousePressEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; void contextMenuEvent(QContextMenuEvent *e) override; private: [[nodiscard]] QImage cacheUnreadCount(int count) const; + [[nodiscard]] int calculateLockedFromX() const; using Index = int; struct Unread final { @@ -51,10 +55,16 @@ private: const int _unreadSkip; std::vector _cachedBadgeWidths; int _cachedBadgeHeight = 0; + int _lockedFrom = 0; + int _lockedFromX = 0; + bool _lockedPressed = false; std::optional _bar; std::optional _barActive; + std::optional _lockCache; + rpl::lifetime _paletteLifetime; rpl::event_stream _contextMenuRequested; + rpl::event_stream<> _lockedClicked; }; diff --git a/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_strip.cpp b/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_strip.cpp index 026bc325e..cd8fdf147 100644 --- a/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_strip.cpp +++ b/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_strip.cpp @@ -9,10 +9,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_chat_filters_remove_manager.h" #include "boxes/filters/edit_filter_box.h" +#include "boxes/premium_limits_box.h" #include "core/application.h" #include "data/data_chat_filters.h" +#include "data/data_premium_limits.h" #include "data/data_session.h" #include "data/data_unread_value.h" +#include "data/data_user.h" #include "lang/lang_keys.h" #include "main/main_session.h" #include "settings/settings_folders.h" @@ -205,6 +208,18 @@ not_null AddChatFiltersTabsStrip( }) | ranges::to_vector; slider->setSections(std::move(sections)); slider->fitWidthToSections(); + { + const auto reorderAll = session->user()->isPremium(); + const auto maxLimit = (reorderAll ? 1 : 0) + + Data::PremiumLimits(session).dialogFiltersCurrent(); + const auto premiumFrom = (reorderAll ? 0 : 1) + maxLimit; + slider->setLockedFrom((premiumFrom >= list.size()) + ? 0 + : premiumFrom); + slider->lockedClicked() | rpl::start_with_next([=] { + controller->show(Box(FiltersLimitBox, session, std::nullopt)); + }, slider->lifetime()); + } { auto includeMuted = Data::IncludeMutedCounterFoldersValue(); state->unreadLifetime.destroy();