diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index 4b6c382da..7d3ed9e64 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -689,7 +689,11 @@ recentPeersSpecialName: PeerListItem(recentPeersItem) { namePosition: point(64px, 19px); } +dialogsTabsScroll: ScrollArea(defaultScrollArea) { + barHidden: true; +} dialogsSearchTabs: SettingsSlider(defaultSettingsSlider) { + padding: 8px; height: 33px; barTop: 30px; barSkip: 0px; @@ -707,7 +711,6 @@ dialogsSearchTabs: SettingsSlider(defaultSettingsSlider) { rippleBgActive: lightButtonBgOver; ripple: defaultRippleAnimation; } -dialogsSearchTabsPadding: 8px; chatsFiltersTabs: SettingsSlider(dialogsSearchTabs) { rippleBottomSkip: 0px; diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp index 192e3fb7f..6b4edc0fe 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp @@ -1292,7 +1292,11 @@ Suggestions::Suggestions( RecentPeersList recentPeers) : RpWidget(parent) , _controller(controller) -, _tabs(std::make_unique(this, st::dialogsSearchTabs)) +, _tabsScroll( + std::make_unique(this, st::dialogsTabsScroll, true)) +, _tabs( + _tabsScroll->setOwnedWidget( + object_ptr(this, st::dialogsSearchTabs))) , _chatsScroll(std::make_unique(this)) , _chatsContent( _chatsScroll->setOwnedWidget(object_ptr(this))) @@ -1314,7 +1318,6 @@ Suggestions::Suggestions( _appsScroll->setOwnedWidget(object_ptr(this))) , _recentApps(setupRecentApps()) , _popularApps(setupPopularApps()) { - setupTabs(); setupChats(); setupChannels(); @@ -1324,10 +1327,46 @@ Suggestions::Suggestions( Suggestions::~Suggestions() = default; void Suggestions::setupTabs() { + _tabsScroll->setCustomWheelProcess([=](not_null e) { + const auto pixelDelta = e->pixelDelta(); + const auto angleDelta = e->angleDelta(); + if (std::abs(pixelDelta.x()) + std::abs(angleDelta.x())) { + return false; + } + const auto y = pixelDelta.y() ? pixelDelta.y() : angleDelta.y(); + _tabsScroll->scrollToX(_tabsScroll->scrollLeft() - y); + return true; + }); + + const auto scrollToIndex = [=](int index, anim::type type) { + const auto to = index + ? (_tabs->centerOfSection(index) - _tabsScroll->width() / 2) + : 0; + _tabsScrollAnimation.stop(); + if (type == anim::type::instant) { + _tabsScroll->scrollToX(to); + } else { + _tabsScrollAnimation.start( + [=](float64 v) { _tabsScroll->scrollToX(v); }, + _tabsScroll->scrollLeft(), + std::min(to, _tabsScroll->scrollLeftMax()), + st::defaultTabsSlider.duration); + } + }; + rpl::single(-1) | rpl::then( + _tabs->sectionActivated() + ) | rpl::combine_previous( + ) | rpl::start_with_next([=](int was, int index) { + if (was != index) { + scrollToIndex(index, anim::type::normal); + } + }, _tabs->lifetime()); + const auto shadow = Ui::CreateChild(this); shadow->lower(); - _tabs->move(st::dialogsSearchTabsPadding, 0); + _tabsScroll->move(0, 0); + _tabs->move(0, 0); rpl::combine( widthValue(), _tabs->heightValue() @@ -1338,11 +1377,12 @@ void Suggestions::setupTabs() { shadow->showOn(_tabs->shownValue()); - _tabs->setSections({ + auto sections = std::vector{ tr::lng_recent_chats(tr::now), tr::lng_recent_channels(tr::now), tr::lng_recent_apps(tr::now), - }); + }; + _tabs->setSections(sections); _tabs->sectionActivated( ) | rpl::start_with_next([=](int section) { switchTab(section == 2 @@ -1808,7 +1848,7 @@ void Suggestions::startShownAnimation(bool shown, Fn finish) { resize(now, height()); } } - _tabs->hide(); + _tabsScroll->hide(); _chatsScroll->hide(); _channelsScroll->hide(); _appsScroll->hide(); @@ -1823,7 +1863,7 @@ void Suggestions::finishShow() { _shownAnimation.stop(); _cache = QPixmap(); - _tabs->show(); + _tabsScroll->show(); const auto tab = _tab.current(); _chatsScroll->setVisible(tab == Tab::Chats); _channelsScroll->setVisible(tab == Tab::Channels); @@ -1864,8 +1904,10 @@ void Suggestions::paintEvent(QPaintEvent *e) { void Suggestions::resizeEvent(QResizeEvent *e) { const auto w = std::max(width(), st::columnMinimalWidthLeft); - _tabs->resizeToWidth(w); + _tabs->fitWidthToSections(); + const auto tabs = _tabs->height(); + _tabsScroll->setGeometry(0, 0, w, tabs); _chatsScroll->setGeometry(0, tabs, w, height() - tabs); _chatsContent->resizeToWidth(w); diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h index 973f65c7d..036ad6972 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h @@ -24,6 +24,7 @@ class Session; namespace Ui { class BoxContent; +class ScrollArea; class ElasticScroll; class SettingsSlider; class VerticalLayout; @@ -169,7 +170,9 @@ private: const not_null _controller; - const std::unique_ptr _tabs; + const std::unique_ptr _tabsScroll; + const not_null _tabs; + Ui::Animations::Simple _tabsScrollAnimation; rpl::variable _tab = Tab::Chats; const std::unique_ptr _chatsScroll; diff --git a/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.cpp b/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.cpp index a6e9bf284..8a35fa520 100644 --- a/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.cpp +++ b/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.cpp @@ -15,18 +15,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include namespace Ui { -namespace { - -[[nodiscard]] QMouseEvent TranslatedMouseEvent(QMouseEvent *e) { - return QMouseEvent( - e->type(), - e->pos() + QPoint(-st::dialogsSearchTabsPadding, 0), - e->button(), - e->buttons(), - e->modifiers()); -} - -} // namespace ChatsFiltersTabs::ChatsFiltersTabs( not_null parent, @@ -77,22 +65,9 @@ bool ChatsFiltersTabs::setSectionsAndCheckChanged( return changed; } -int ChatsFiltersTabs::centerOfSection(int section) const { - const auto widths = countSectionsWidths(0); - auto result = 0; - if (section >= 0 && section < widths.size()) { - for (auto i = 0; i < section; i++) { - result += widths[i]; - } - result += widths[section] / 2; - } - return result; -} - void ChatsFiltersTabs::fitWidthToSections() { - const auto widths = countSectionsWidths(0); - const auto sliderPadding = st::dialogsSearchTabsPadding; - resizeToWidth(ranges::accumulate(widths, .0) + sliderPadding * 2); + SettingsSlider::fitWidthToSections(); + _lockedFromX = calculateLockedFromX(); { @@ -197,8 +172,6 @@ void ChatsFiltersTabs::paintEvent(QPaintEvent *e) { const auto range = getCurrentActiveRange(); const auto activeIndex = activeSection(); - p.translate(st::dialogsSearchTabsPadding, 0); - auto index = 0; auto raisedIndex = -1; auto activeHorizontalShift = 0; @@ -310,32 +283,29 @@ void ChatsFiltersTabs::paintEvent(QPaintEvent *e) { } void ChatsFiltersTabs::mousePressEvent(QMouseEvent *e) { - auto m = TranslatedMouseEvent(e); - const auto mouseButton = m.button(); + const auto mouseButton = e->button(); if (mouseButton == Qt::MouseButton::LeftButton) { - _lockedPressed = (m.pos().x() >= _lockedFromX); + _lockedPressed = (e->pos().x() >= _lockedFromX); if (_lockedPressed) { - Ui::RpWidget::mousePressEvent(&m); + Ui::RpWidget::mousePressEvent(e); } else { - Ui::SettingsSlider::mousePressEvent(&m); + Ui::SettingsSlider::mousePressEvent(e); } } else { - Ui::RpWidget::mousePressEvent(&m); + Ui::RpWidget::mousePressEvent(e); } } void ChatsFiltersTabs::mouseMoveEvent(QMouseEvent *e) { - auto m = TranslatedMouseEvent(e); if (_reordering) { - Ui::RpWidget::mouseMoveEvent(&m); + Ui::RpWidget::mouseMoveEvent(e); } else { - Ui::SettingsSlider::mouseMoveEvent(&m); + Ui::SettingsSlider::mouseMoveEvent(e); } } void ChatsFiltersTabs::mouseReleaseEvent(QMouseEvent *e) { - auto m = TranslatedMouseEvent(e); - const auto mouseButton = m.button(); + const auto mouseButton = e->button(); if (mouseButton == Qt::MouseButton::LeftButton) { if (base::take(_lockedPressed)) { _lockedPressed = false; @@ -348,16 +318,16 @@ void ChatsFiltersTabs::mouseReleaseEvent(QMouseEvent *e) { } } } else { - Ui::SettingsSlider::mouseReleaseEvent(&m); + Ui::SettingsSlider::mouseReleaseEvent(e); } } } else { - Ui::RpWidget::mouseReleaseEvent(&m); + Ui::RpWidget::mouseReleaseEvent(e); } } void ChatsFiltersTabs::contextMenuEvent(QContextMenuEvent *e) { - const auto pos = e->pos() + QPoint(-st::dialogsSearchTabsPadding, 0); + const auto pos = e->pos(); if (pos.x() >= _lockedFromX) { return; } diff --git a/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.h b/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.h index 9bd4cf67e..0f6df2331 100644 --- a/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.h +++ b/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.h @@ -29,8 +29,7 @@ public: bool setSectionsAndCheckChanged(std::vector &§ions); - [[nodiscard]] int centerOfSection(int section) const; - void fitWidthToSections(); + void fitWidthToSections() override; void setUnreadCount(int index, int unreadCount, bool muted); void setLockedFrom(int index); diff --git a/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_strip.cpp b/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_strip.cpp index f9ecc27bf..f80b12a03 100644 --- a/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_strip.cpp +++ b/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_strip.cpp @@ -180,7 +180,6 @@ not_null AddChatFiltersTabsStrip( Window::SessionController *controller, bool trackActiveFilterAndUnreadAndReorder) { - const auto &scrollSt = st::defaultScrollArea; const auto wrap = Ui::CreateChild>( parent, object_ptr(parent)); @@ -194,7 +193,7 @@ not_null AddChatFiltersTabsStrip( const auto container = wrap->entity(); const auto scroll = Ui::CreateChild( container, - scrollSt, + st::dialogsTabsScroll, true); const auto slider = scroll->setOwnedWidget( object_ptr( @@ -457,7 +456,7 @@ not_null AddChatFiltersTabsStrip( parent->widthValue() | rpl::filter(rpl::mappers::_1 > 0), slider->heightValue() | rpl::filter(rpl::mappers::_1 > 0) ) | rpl::start_with_next([=](int w, int h) { - scroll->resize(w, h + scrollSt.deltax * 4); + scroll->resize(w, h); container->resize(w, h); wrap->resize(w, h); }, wrap->lifetime()); diff --git a/Telegram/SourceFiles/ui/widgets/discrete_sliders.cpp b/Telegram/SourceFiles/ui/widgets/discrete_sliders.cpp index f686cefd0..e11ea96d0 100644 --- a/Telegram/SourceFiles/ui/widgets/discrete_sliders.cpp +++ b/Telegram/SourceFiles/ui/widgets/discrete_sliders.cpp @@ -242,6 +242,27 @@ SettingsSlider::SettingsSlider( setSelectOnPress(_st.ripple.showDuration == 0); } +const style::SettingsSlider &SettingsSlider::st() const { + return _st; +} + +int SettingsSlider::centerOfSection(int section) const { + const auto widths = countSectionsWidths(0); + auto result = 0; + if (section >= 0 && section < widths.size()) { + for (auto i = 0; i < section; i++) { + result += widths[i]; + } + result += widths[section] / 2; + } + return result; +} + +void SettingsSlider::fitWidthToSections() { + const auto widths = countSectionsWidths(0); + resizeToWidth(ranges::accumulate(widths, .0) + _st.padding * 2); +} + void SettingsSlider::setRippleTopRoundRadius(int radius) { _rippleTopRoundRadius = radius; } @@ -263,7 +284,7 @@ void SettingsSlider::resizeSections(int newWidth) { const auto sectionWidths = countSectionsWidths(newWidth); auto skip = 0; - auto x = 0.; + auto x = _st.padding * 1.; auto sectionWidth = sectionWidths.begin(); enumerateSections([&](Section §ion) { Expects(sectionWidth != sectionWidths.end()); @@ -280,7 +301,9 @@ void SettingsSlider::resizeSections(int newWidth) { std::vector SettingsSlider::countSectionsWidths(int newWidth) const { const auto count = getSectionsCount(); - const auto sectionsWidth = newWidth - (count - 1) * _st.barSkip; + const auto sectionsWidth = newWidth + - 2 * _st.padding + - (count - 1) * _st.barSkip; const auto sectionWidth = sectionsWidth / float64(count); auto result = std::vector(count, sectionWidth); diff --git a/Telegram/SourceFiles/ui/widgets/discrete_sliders.h b/Telegram/SourceFiles/ui/widgets/discrete_sliders.h index c89b6cfef..2ff6f771c 100644 --- a/Telegram/SourceFiles/ui/widgets/discrete_sliders.h +++ b/Telegram/SourceFiles/ui/widgets/discrete_sliders.h @@ -130,6 +130,11 @@ public: QWidget *parent, const style::SettingsSlider &st = st::defaultSettingsSlider); + [[nodiscard]] const style::SettingsSlider &st() const; + + [[nodiscard]] int centerOfSection(int section) const; + virtual void fitWidthToSections(); + void setRippleTopRoundRadius(int radius); protected: