mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/telegramdesktop/tdesktop
synced 2026-09-20 08:03:45 +08:00
Added profile media tabs behind experimental option.
This commit is contained in:
@@ -210,6 +210,9 @@ protected:
|
||||
void scrollTopRestore(int scrollTop);
|
||||
void scrollTo(const Ui::ScrollToRequest &request);
|
||||
[[nodiscard]] rpl::producer<int> scrollTopValue() const;
|
||||
[[nodiscard]] int innerTopReserve() const {
|
||||
return _innerTopReserve;
|
||||
}
|
||||
|
||||
void setPaintPadding(const style::margins &padding);
|
||||
|
||||
|
||||
@@ -10,12 +10,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "info/info_controller.h"
|
||||
#include "info/info_memento.h"
|
||||
#include "info/profile/info_profile_widget.h"
|
||||
#include "info/profile/tabs/adapters/info_profile_tab_media.h"
|
||||
#include "info/profile/tabs/adapters/info_profile_tab_members.h"
|
||||
#include "info/profile/tabs/adapters/info_profile_tab_peer_lists.h"
|
||||
#include "info/profile/tabs/adapters/info_profile_tab_polls.h"
|
||||
#include "info/profile/tabs/adapters/info_profile_tab_saved.h"
|
||||
#include "info/profile/tabs/adapters/info_profile_tab_stories.h"
|
||||
#include "info/profile/tabs/info_profile_tabs_host.h"
|
||||
#include "info/profile/info_profile_icon.h"
|
||||
#include "info/profile/info_profile_members.h"
|
||||
#include "info/profile/info_profile_music_button.h"
|
||||
#include "info/profile/info_profile_shared_media_classic.h"
|
||||
#include "info/profile/info_profile_top_bar.h"
|
||||
#include "info/profile/info_profile_actions.h"
|
||||
#include "info/profile/info_profile_shared_media_classic.h"
|
||||
#include "info/profile/info_profile_values.h"
|
||||
#include "info/saved/info_saved_music_widget.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_channel.h"
|
||||
@@ -214,8 +222,9 @@ object_ptr<Ui::RpWidget> InnerWidget::setupContent(
|
||||
_sublist,
|
||||
origin);
|
||||
|
||||
const auto tabs = UseProfileMediaTabs();
|
||||
auto sharedTracker = Ui::MultiSlideTracker();
|
||||
{
|
||||
if (!tabs) {
|
||||
auto sharedMediaWidget = SetupSharedMediaClassic(
|
||||
result.data(),
|
||||
_controller,
|
||||
@@ -232,7 +241,94 @@ object_ptr<Ui::RpWidget> InnerWidget::setupContent(
|
||||
.shown = raw->toggledValue(),
|
||||
});
|
||||
}
|
||||
|
||||
const auto addTabsHost = [&] {
|
||||
using namespace rpl::mappers;
|
||||
const auto tabsPeer = _sublist ? _sublist->sublistPeer() : _peer;
|
||||
const auto topicRootId = _topic ? _topic->rootId() : MsgId();
|
||||
const auto monoforumPeerId = _sublist
|
||||
? _sublist->sublistPeer()->id
|
||||
: PeerId();
|
||||
auto tabs = std::vector<MediaTabDescriptor>();
|
||||
const auto addTab = [&](Storage::SharedMediaType type) {
|
||||
tabs.push_back(MakeMediaTabDescriptor(
|
||||
type,
|
||||
SharedMediaCountValue(
|
||||
tabsPeer,
|
||||
topicRootId,
|
||||
monoforumPeerId,
|
||||
_migrated,
|
||||
type) | rpl::map(_1 > 0)));
|
||||
};
|
||||
if ((_peer->isChat() || _peer->isMegagroup())
|
||||
&& !_peer->isMonoforum()
|
||||
&& !_topic
|
||||
&& !_sublist) {
|
||||
tabs.push_back(MakeMembersTabDescriptor(_peer));
|
||||
}
|
||||
if (!_topic) {
|
||||
tabs.push_back(MakeStoriesTabDescriptor(tabsPeer));
|
||||
if (!_sublist) {
|
||||
tabs.push_back(MakeGiftsTabDescriptor(_peer));
|
||||
}
|
||||
tabs.push_back(MakeSavedTabDescriptor(tabsPeer));
|
||||
}
|
||||
addTab(Storage::SharedMediaType::Photo);
|
||||
addTab(Storage::SharedMediaType::Video);
|
||||
addTab(Storage::SharedMediaType::File);
|
||||
addTab(Storage::SharedMediaType::MusicFile);
|
||||
addTab(Storage::SharedMediaType::Link);
|
||||
tabs.push_back(MakePollsTabDescriptor(SharedMediaCountValue(
|
||||
tabsPeer,
|
||||
topicRootId,
|
||||
monoforumPeerId,
|
||||
_migrated,
|
||||
Storage::SharedMediaType::Poll) | rpl::map(_1 > 0)));
|
||||
addTab(Storage::SharedMediaType::RoundVoiceFile);
|
||||
addTab(Storage::SharedMediaType::GIF);
|
||||
if (!_topic && !_sublist) {
|
||||
if (const auto user = _peer->asUser()) {
|
||||
tabs.push_back(MakeCommonGroupsTabDescriptor(user));
|
||||
}
|
||||
if (_peer->asBot() || _peer->asBroadcast()) {
|
||||
tabs.push_back(MakeSimilarPeersTabDescriptor(_peer));
|
||||
}
|
||||
}
|
||||
auto tabsHost = object_ptr<TabsHost>(
|
||||
result.data(),
|
||||
TabsHost::Descriptor{
|
||||
.context = MediaTabContext{
|
||||
.controller = _controller,
|
||||
.peer = _peer,
|
||||
.topic = _topic,
|
||||
.sublist = _sublist,
|
||||
.migrated = _migrated,
|
||||
.onlineCountChanged = [this](int count) {
|
||||
_onlineCount.fire_copy(count);
|
||||
},
|
||||
},
|
||||
.tabs = std::move(tabs),
|
||||
});
|
||||
const auto raw = tabsHost.data();
|
||||
_tabsHost = raw;
|
||||
raw->scrollToRequests(
|
||||
) | rpl::on_next([this, raw](Ui::ScrollToRequest request) {
|
||||
const auto shift = MapFrom(this, raw, QPoint()).y();
|
||||
_scrollToRequests.fire({
|
||||
request.ymin + shift,
|
||||
(request.ymax < 0) ? -1 : (request.ymax + shift),
|
||||
});
|
||||
}, raw->lifetime());
|
||||
stack.addPlainSeparator();
|
||||
stack.add(Section{
|
||||
.widget = std::move(tabsHost),
|
||||
.shown = raw->heightValue() | rpl::map(_1 > 0),
|
||||
});
|
||||
};
|
||||
if (_topic || _sublist) {
|
||||
if (tabs) {
|
||||
addTabsHost();
|
||||
}
|
||||
stack.finalize();
|
||||
return result;
|
||||
}
|
||||
@@ -255,7 +351,9 @@ object_ptr<Ui::RpWidget> InnerWidget::setupContent(
|
||||
.shown = rpl::single(true),
|
||||
});
|
||||
}
|
||||
if ((_peer->isChat() || _peer->isMegagroup())
|
||||
if (tabs) {
|
||||
addTabsHost();
|
||||
} else if ((_peer->isChat() || _peer->isMegagroup())
|
||||
&& !_peer->isMonoforum()) {
|
||||
stack.addPlainSeparator();
|
||||
stack.add(makeMembersSection(result.data()));
|
||||
@@ -308,12 +406,20 @@ void InnerWidget::visibleTopBottomUpdated(
|
||||
int visibleTop,
|
||||
int visibleBottom) {
|
||||
setChildVisibleTopBottom(_content, visibleTop, visibleBottom);
|
||||
if (_tabsHost) {
|
||||
const auto top = MapFrom(this, _tabsHost, QPoint()).y();
|
||||
_tabsHost->setVisibleRegion(visibleTop - top, visibleBottom - top);
|
||||
_tabsDocked = (visibleTop >= top);
|
||||
}
|
||||
}
|
||||
|
||||
void InnerWidget::saveState(not_null<Memento*> memento) {
|
||||
if (_members) {
|
||||
memento->setMembersState(_members->saveState());
|
||||
}
|
||||
if (_tabsHost) {
|
||||
memento->setActiveTab(_tabsHost->activeId());
|
||||
}
|
||||
}
|
||||
|
||||
void InnerWidget::restoreState(not_null<Memento*> memento) {
|
||||
@@ -323,6 +429,11 @@ void InnerWidget::restoreState(not_null<Memento*> memento) {
|
||||
if (_sharedMediaWrap) {
|
||||
_sharedMediaWrap->finishAnimating();
|
||||
}
|
||||
if (_tabsHost) {
|
||||
if (const auto active = memento->activeTab(); !active.isEmpty()) {
|
||||
_tabsHost->restoreActiveTab(active);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rpl::producer<Ui::ScrollToRequest> InnerWidget::scrollToRequests() const {
|
||||
@@ -370,6 +481,11 @@ base::weak_qptr<Ui::RpWidget> InnerWidget::createPinnedToTop(
|
||||
content->backRequest(
|
||||
) | rpl::start_to_stream(_backClicks, content->lifetime());
|
||||
content->setOnlineCount(_onlineCount.events());
|
||||
if (_tabsHost) {
|
||||
content->bindActiveTab(
|
||||
_tabsHost->activeTabBindings(),
|
||||
_tabsDocked.value());
|
||||
}
|
||||
_topBarColor = content->edgeColor();
|
||||
return base::make_weak(not_null<Ui::RpWidget*>{ content });
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ namespace Profile {
|
||||
|
||||
class Memento;
|
||||
class Members;
|
||||
class TabsHost;
|
||||
struct Origin;
|
||||
|
||||
class InnerWidget final : public Ui::RpWidget {
|
||||
@@ -64,6 +65,13 @@ public:
|
||||
void enableBackButton();
|
||||
void showFinished();
|
||||
|
||||
[[nodiscard]] TabsHost *tabsHost() const {
|
||||
return _tabsHost;
|
||||
}
|
||||
[[nodiscard]] rpl::producer<bool> tabsDockedValue() const {
|
||||
return _tabsDocked.value();
|
||||
}
|
||||
|
||||
protected:
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
void visibleTopBottomUpdated(
|
||||
@@ -78,7 +86,11 @@ private:
|
||||
|
||||
int countDesiredHeight() const;
|
||||
void updateDesiredHeight() {
|
||||
_desiredHeight.fire(countDesiredHeight());
|
||||
const auto value = countDesiredHeight();
|
||||
if (_lastDesiredHeight != value) {
|
||||
_lastDesiredHeight = value;
|
||||
_desiredHeight.fire_copy(value);
|
||||
}
|
||||
}
|
||||
|
||||
const not_null<Controller*> _controller;
|
||||
@@ -88,6 +100,7 @@ private:
|
||||
Data::SavedSublist * const _sublist = nullptr;
|
||||
|
||||
bool _inResize = false;
|
||||
int _lastDesiredHeight = -1;
|
||||
rpl::event_stream<Ui::ScrollToRequest> _scrollToRequests;
|
||||
rpl::event_stream<int> _desiredHeight;
|
||||
|
||||
@@ -102,6 +115,8 @@ private:
|
||||
|
||||
Members *_members = nullptr;
|
||||
Ui::SlideWrap<RpWidget> *_sharedMediaWrap = nullptr;
|
||||
TabsHost *_tabsHost = nullptr;
|
||||
rpl::variable<bool> _tabsDocked = false;
|
||||
object_ptr<RpWidget> _content;
|
||||
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history.h"
|
||||
#include "info/profile/info_profile_inner_widget.h"
|
||||
#include "info/profile/info_profile_members.h"
|
||||
#include "info/profile/tabs/info_profile_tabs_host.h"
|
||||
#include "info/settings/info_settings_widget.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "ui/ui_utility.h"
|
||||
@@ -107,6 +108,7 @@ Widget::Widget(
|
||||
controller->setSearchEnabledByContent(false);
|
||||
|
||||
const auto classic = UseClassicProfileScroll();
|
||||
const auto tabs = (_inner->tabsHost() != nullptr);
|
||||
const auto flexible = _pinnedToTop
|
||||
&& _pinnedToTop->minimumHeight()
|
||||
&& _inner->hasFlexibleTopBar();
|
||||
@@ -115,12 +117,19 @@ Widget::Widget(
|
||||
}
|
||||
|
||||
_inner->scrollToRequests(
|
||||
) | rpl::on_next([this](Ui::ScrollToRequest request) {
|
||||
) | rpl::on_next([this, tabs](Ui::ScrollToRequest request) {
|
||||
if (request.ymin < 0) {
|
||||
scrollTopRestore(
|
||||
qMin(scrollTopSave(), request.ymax));
|
||||
} else {
|
||||
} else if (!tabs) {
|
||||
scrollTo(request);
|
||||
} else {
|
||||
// Inner coordinates miss the flexible cover top padding.
|
||||
const auto reserve = innerTopReserve();
|
||||
scrollTo({
|
||||
request.ymin + reserve,
|
||||
(request.ymax < 0) ? -1 : (request.ymax + reserve),
|
||||
});
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
@@ -128,8 +137,12 @@ Widget::Widget(
|
||||
checkBeforeClose([=] { controller->showBackFromStack(); });
|
||||
}, _inner->lifetime());
|
||||
|
||||
setSwipeInterceptor([this](Ui::Controls::SwipeHandlerInitData data) {
|
||||
return swipeTabsFinishData(data);
|
||||
});
|
||||
|
||||
if (!classic && flexible) {
|
||||
setupFlexibleRegularScroll(_inner, _pinnedToTop.get());
|
||||
setupFlexibleRegularScroll(_inner, _pinnedToTop.get(), tabs);
|
||||
} else if (_pinnedToTop) {
|
||||
_inner->widthValue(
|
||||
) | rpl::on_next([=](int w) {
|
||||
@@ -154,7 +167,8 @@ Widget::Widget(
|
||||
[=](rpl::producer<not_null<QEvent*>> &&events) {
|
||||
ContentWidget::setViewport(std::move(events));
|
||||
},
|
||||
_flexibleScroll);
|
||||
_flexibleScroll,
|
||||
tabs);
|
||||
}
|
||||
|
||||
if (_pinnedToBottom) {
|
||||
@@ -175,6 +189,89 @@ Widget::Widget(
|
||||
heightValue()
|
||||
) | rpl::on_next(processHeight, _pinnedToBottom->lifetime());
|
||||
}
|
||||
|
||||
setupTabsStripFloat();
|
||||
}
|
||||
|
||||
void Widget::setupTabsStripFloat() {
|
||||
const auto host = _inner->tabsHost();
|
||||
if (!host || !_pinnedToTop) {
|
||||
return;
|
||||
}
|
||||
_tabsHost = base::make_weak(host);
|
||||
_tabsStrip = base::make_weak(host->stripWidget().get());
|
||||
_inner->tabsDockedValue(
|
||||
) | rpl::distinct_until_changed(
|
||||
) | rpl::on_next([=](bool docked) {
|
||||
const auto host = _tabsHost.get();
|
||||
const auto strip = _tabsStrip.get();
|
||||
if (!host || !strip) {
|
||||
return;
|
||||
}
|
||||
if (docked) {
|
||||
if (!_tabsStripFloat) {
|
||||
_tabsStripFloat = base::make_unique_q<Ui::RpWidget>(this);
|
||||
ContentWidget::setViewport(_tabsStripFloat->events(
|
||||
) | rpl::filter([](not_null<QEvent*> e) {
|
||||
return (e->type() == QEvent::Wheel);
|
||||
}));
|
||||
}
|
||||
strip->setParent(_tabsStripFloat.get());
|
||||
strip->moveToLeft(0, 0);
|
||||
strip->show();
|
||||
_tabsStripFloat->show();
|
||||
_tabsStripFloat->raise();
|
||||
updateTabsStripFloatGeometry();
|
||||
} else if (_tabsStripFloat) {
|
||||
_tabsStripFloat->hide();
|
||||
host->returnStrip();
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
rpl::combine(
|
||||
widthValue(),
|
||||
host->stripWidget()->heightValue(),
|
||||
host->stripWidget()->naturalWidthValue()
|
||||
) | rpl::on_next([=] {
|
||||
updateTabsStripFloatGeometry();
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
void Widget::updateTabsStripFloatGeometry() {
|
||||
const auto strip = _tabsStrip.get();
|
||||
if (!strip
|
||||
|| !_tabsStripFloat
|
||||
|| _tabsStripFloat->isHidden()
|
||||
|| !_pinnedToTop) {
|
||||
return;
|
||||
}
|
||||
const auto stripWidth = std::min(strip->naturalWidth(), width());
|
||||
strip->resizeToWidth(stripWidth);
|
||||
strip->moveToLeft(0, 0);
|
||||
_tabsStripFloat->setGeometry(
|
||||
(width() - stripWidth) / 2,
|
||||
_pinnedToTop->minimumHeight(),
|
||||
stripWidth,
|
||||
strip->height());
|
||||
}
|
||||
|
||||
auto Widget::swipeTabsFinishData(Ui::Controls::SwipeHandlerInitData data)
|
||||
-> Ui::Controls::SwipeHandlerFinishData {
|
||||
const auto host = _inner->tabsHost();
|
||||
if (!host) {
|
||||
return {};
|
||||
}
|
||||
const auto shift = Ui::MapFrom(_inner, host, QPoint());
|
||||
const auto body = host->bodyGeometry().translated(shift);
|
||||
if (!body.contains(data.cursorPosition)) {
|
||||
return {};
|
||||
}
|
||||
const auto toNextTab = (data.direction == Qt::LeftToRight);
|
||||
auto callback = host->prepareSwitch(toNextTab);
|
||||
return callback
|
||||
? Ui::Controls::DefaultSwipeBackHandlerFinishData(
|
||||
std::move(callback))
|
||||
: Ui::Controls::SwipeHandlerFinishData();
|
||||
}
|
||||
|
||||
void Widget::setInnerFocus() {
|
||||
|
||||
@@ -17,6 +17,7 @@ class ForumTopic;
|
||||
namespace Info::Profile {
|
||||
|
||||
class InnerWidget;
|
||||
class TabsHost;
|
||||
struct MembersState;
|
||||
|
||||
struct GroupReactionOrigin {
|
||||
@@ -50,7 +51,14 @@ public:
|
||||
}
|
||||
|
||||
void setMembersState(std::unique_ptr<MembersState> state);
|
||||
std::unique_ptr<MembersState> membersState();
|
||||
[[nodiscard]] std::unique_ptr<MembersState> membersState();
|
||||
|
||||
void setActiveTab(const QString &id) {
|
||||
_activeTab = id;
|
||||
}
|
||||
[[nodiscard]] QString activeTab() const {
|
||||
return _activeTab;
|
||||
}
|
||||
|
||||
~Memento();
|
||||
|
||||
@@ -64,6 +72,7 @@ private:
|
||||
|
||||
std::unique_ptr<MembersState> _membersState;
|
||||
Origin _origin;
|
||||
QString _activeTab;
|
||||
|
||||
};
|
||||
|
||||
@@ -88,6 +97,11 @@ public:
|
||||
private:
|
||||
void saveState(not_null<Memento*> memento);
|
||||
void restoreState(not_null<Memento*> memento);
|
||||
void setupTabsStripFloat();
|
||||
void updateTabsStripFloatGeometry();
|
||||
[[nodiscard]] auto swipeTabsFinishData(
|
||||
Ui::Controls::SwipeHandlerInitData data)
|
||||
-> Ui::Controls::SwipeHandlerFinishData;
|
||||
|
||||
std::shared_ptr<ContentMemento> doCreateMemento() override;
|
||||
|
||||
@@ -96,6 +110,9 @@ private:
|
||||
base::weak_qptr<Ui::RpWidget> _pinnedToTop;
|
||||
base::weak_qptr<Ui::RpWidget> _pinnedToBottom;
|
||||
std::unique_ptr<FlexibleScrollHelper> _flexibleScrollHelper;
|
||||
base::unique_qptr<Ui::RpWidget> _tabsStripFloat;
|
||||
base::weak_qptr<TabsHost> _tabsHost;
|
||||
base::weak_qptr<Ui::RpWidget> _tabsStrip;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "info/profile/tabs/info_profile_tabs_host.h"
|
||||
|
||||
#include "info/profile/tabs/info_profile_tabs_strip.h"
|
||||
#include "base/options.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "ui/effects/slide_animation.h"
|
||||
#include "ui/painter.h"
|
||||
@@ -16,6 +17,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_info.h"
|
||||
|
||||
namespace Info::Profile {
|
||||
namespace {
|
||||
|
||||
base::options::toggle ProfileMediaTabs({
|
||||
.id = kOptionProfileMediaTabs,
|
||||
.name = "Show shared media as tabs in the profile.",
|
||||
.description = "Replace the shared media buttons in profiles with "
|
||||
"a strip of tabs holding the media lists inline. Work in progress.",
|
||||
});
|
||||
|
||||
} // namespace
|
||||
|
||||
const char kOptionProfileMediaTabs[] = "profile-media-tabs";
|
||||
|
||||
bool UseProfileMediaTabs() {
|
||||
return ProfileMediaTabs.value();
|
||||
}
|
||||
|
||||
TabsHost::TabsHost(not_null<QWidget*> parent, Descriptor descriptor)
|
||||
: RpWidget(parent)
|
||||
|
||||
@@ -20,6 +20,10 @@ struct ScrollToRequest;
|
||||
|
||||
namespace Info::Profile {
|
||||
|
||||
extern const char kOptionProfileMediaTabs[];
|
||||
|
||||
[[nodiscard]] bool UseProfileMediaTabs();
|
||||
|
||||
class TabsStrip;
|
||||
|
||||
class TabsHost final : public Ui::RpWidget {
|
||||
|
||||
@@ -39,6 +39,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/view/controls/compose_controls_common.h"
|
||||
#include "history/view/history_view_message.h"
|
||||
#include "info/profile/info_profile_actions.h"
|
||||
#include "info/profile/tabs/info_profile_tabs_host.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mainwindow.h"
|
||||
#include "mainwidget.h"
|
||||
@@ -286,6 +287,7 @@ void SetupExperimental(
|
||||
addToggle(Window::kOptionViewProfileInChatsListContextMenu);
|
||||
addToggle(Info::Profile::kOptionShowPeerIdBelowAbout);
|
||||
addToggle(Info::Profile::kOptionShowChannelJoinedBelowAbout);
|
||||
addToggle(Info::Profile::kOptionProfileMediaTabs);
|
||||
addToggle(Ui::kOptionUseSmallMsgBubbleRadius);
|
||||
addToggle(Media::Player::kOptionDisableAutoplayNext);
|
||||
addToggle(Webview::kOptionWebviewDebugEnabled);
|
||||
|
||||
Reference in New Issue
Block a user