From 0eef766ea18091a0295c5c4a2e12fb09d9e0e793 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 14 Sep 2025 11:46:12 +0300 Subject: [PATCH] Added ui ability to reorder tabs in subsection slider. --- .../view/history_view_subsection_tabs.cpp | 67 +++++++++++++++++-- .../view/history_view_subsection_tabs.h | 7 ++ 2 files changed, 67 insertions(+), 7 deletions(-) diff --git a/Telegram/SourceFiles/history/view/history_view_subsection_tabs.cpp b/Telegram/SourceFiles/history/view/history_view_subsection_tabs.cpp index d38cc7d371..52067ca715 100644 --- a/Telegram/SourceFiles/history/view/history_view_subsection_tabs.cpp +++ b/Telegram/SourceFiles/history/view/history_view_subsection_tabs.cpp @@ -9,33 +9,35 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/qt/qt_key_modifiers.h" #include "core/ui_integration.h" -#include "data/stickers/data_custom_emoji.h" #include "data/data_changes.h" #include "data/data_channel.h" -#include "data/data_forum.h" #include "data/data_forum_topic.h" +#include "data/data_forum.h" #include "data/data_saved_messages.h" #include "data/data_saved_sublist.h" #include "data/data_session.h" #include "data/data_thread.h" #include "data/data_user.h" +#include "data/stickers/data_custom_emoji.h" #include "dialogs/dialogs_main_list.h" #include "history/history.h" #include "lang/lang_keys.h" -#include "main/main_session.h" #include "main/main_session_settings.h" +#include "main/main_session.h" +#include "ui/controls/subsection_tabs_slider_reorder.h" #include "ui/controls/subsection_tabs_slider.h" +#include "ui/dynamic_image.h" +#include "ui/dynamic_thumbnails.h" #include "ui/effects/ripple_animation.h" -#include "ui/widgets/menu/menu_add_action_callback_factory.h" -#include "ui/widgets/menu/menu_add_action_callback.h" #include "ui/text/text_utilities.h" +#include "ui/ui_utility.h" #include "ui/widgets/buttons.h" #include "ui/widgets/discrete_sliders.h" +#include "ui/widgets/menu/menu_add_action_callback_factory.h" +#include "ui/widgets/menu/menu_add_action_callback.h" #include "ui/widgets/popup_menu.h" #include "ui/widgets/scroll_area.h" #include "ui/widgets/shadow.h" -#include "ui/dynamic_image.h" -#include "ui/dynamic_thumbnails.h" #include "window/window_peer_menu.h" #include "window/window_separate_id.h" #include "window/window_session_controller.h" @@ -113,6 +115,7 @@ void SubsectionTabs::setupHorizontal(not_null parent) { const auto shadow = Ui::CreateChild(_horizontal); const auto slider = scroll->setOwnedWidget( object_ptr(scroll)); + _reorder = std::make_unique(slider); setupSlider(scroll, slider, false); shadow->showOn(rpl::single( @@ -185,6 +188,7 @@ void SubsectionTabs::setupVertical(not_null parent) { const auto shadow = Ui::CreateChild(_vertical); const auto slider = scroll->setOwnedWidget( object_ptr(scroll)); + _reorder = std::make_unique(slider); setupSlider(scroll, slider, true); shadow->showOn(rpl::single( @@ -220,6 +224,9 @@ void SubsectionTabs::setupSlider( not_null slider, bool vertical) { slider->sectionActivated() | rpl::start_with_next([=](int active) { + if (_reordering) { + return; + } const auto newWindow = base::IsCtrlPressed(); if (active >= 0 && active < _slice.size() @@ -235,8 +242,28 @@ void SubsectionTabs::setupSlider( _controller->showThread(thread, ShowAtUnreadMsgId, params); } } + _reorder->finishReordering(); }, slider->lifetime()); + _reorder->updates( + ) | rpl::start_with_next([=](Ui::SubsectionSliderReorder::Single data) { + using State = Ui::SubsectionSliderReorder::State; + if (data.state == State::Started) { + ++_reordering; + } else { + Ui::PostponeCall(slider, [=] { + --_reordering; + }); + if (data.state == State::Applied) { + applyReorder(data.widget, data.oldPosition, data.newPosition); + } + } + }, slider->lifetime()); + + slider->setIsReorderingCallback([=] { + return _reordering > 0; + }); + slider->sectionContextMenu() | rpl::start_with_next([=](int index) { if (index >= 0 && index < _slice.size()) { showThreadContextMenu(_slice[index].thread); @@ -474,12 +501,22 @@ void SubsectionTabs::startFillingSlider( .fixed = fixedCount, .pinned = pinnedCount, }, paused); + _reorder->clearPinnedIntervals(); + _reorder->addPinnedInterval(0, 1); + if (pinnedCount > 1) { + const auto from = 1 + pinnedCount; + _reorder->addPinnedInterval(from, slider->sectionsCount() - from); + } const auto ignoreActiveScroll = (scrollSavingIndex >= 0); slider->setActiveSectionFast(activeIndex, ignoreActiveScroll); _sectionsSlice = _slice; Assert(slider->sectionsCount() == _slice.size()); + + _reorder->cancel(); + _reorder->start(); + if (ignoreActiveScroll) { Assert(scrollSavingIndex < slider->sectionsCount()); const auto position = scrollSavingShift @@ -870,4 +907,20 @@ bool SubsectionTabs::UsedFor(not_null thread) { || history->peer->useSubsectionTabs(); } +void SubsectionTabs::applyReorder( + not_null widget, + int oldPosition, + int newPosition) { + if (newPosition == oldPosition) { + return; + } + + Assert(oldPosition >= 0 && oldPosition < _slice.size()); + Assert(newPosition >= 0 && newPosition < _slice.size()); + + base::reorder(_slice, oldPosition, newPosition); + + // TODO: Api. +} + } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_subsection_tabs.h b/Telegram/SourceFiles/history/view/history_view_subsection_tabs.h index 711a0ce132..01be891f4b 100644 --- a/Telegram/SourceFiles/history/view/history_view_subsection_tabs.h +++ b/Telegram/SourceFiles/history/view/history_view_subsection_tabs.h @@ -29,6 +29,7 @@ class RpWidget; class PopupMenu; class ScrollArea; class SubsectionSlider; +class SubsectionSliderReorder; } // namespace Ui namespace HistoryView { @@ -105,6 +106,10 @@ private: not_null slider, bool vertical); void showThreadContextMenu(not_null thread); + void applyReorder( + not_null widget, + int oldPosition, + int newPosition); const not_null _controller; const not_null _history; @@ -114,6 +119,8 @@ private: Ui::RpWidget *_horizontal = nullptr; Ui::RpWidget *_vertical = nullptr; Ui::RpWidget *_shadow = nullptr; + std::unique_ptr _reorder; + int _reordering = 0; std::vector _slice; std::vector _sectionsSlice;