Start new tabs for monoforums.

This commit is contained in:
John Preston 2025-05-23 14:06:46 +04:00
parent 3dbdecf73d
commit fdbdeeb956
28 changed files with 869 additions and 181 deletions

View file

@ -888,6 +888,8 @@ PRIVATE
history/view/history_view_sponsored_click_handler.h history/view/history_view_sponsored_click_handler.h
history/view/history_view_sticker_toast.cpp history/view/history_view_sticker_toast.cpp
history/view/history_view_sticker_toast.h history/view/history_view_sticker_toast.h
history/view/history_view_subsection_tabs.cpp
history/view/history_view_subsection_tabs.h
history/view/history_view_text_helper.cpp history/view/history_view_text_helper.cpp
history/view/history_view_text_helper.h history/view/history_view_text_helper.h
history/view/history_view_transcribe_button.cpp history/view/history_view_transcribe_button.cpp

View file

@ -52,7 +52,7 @@ public:
bool elementAnimationsPaused() override; bool elementAnimationsPaused() override;
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override; not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
HistoryView::Context elementContext() override; HistoryView::Context elementContext() override;
bool elementIsChatWide() override; HistoryView::ElementChatMode elementChatMode() override;
private: private:
const not_null<QWidget*> _parent; const not_null<QWidget*> _parent;
@ -83,8 +83,9 @@ HistoryView::Context PreviewDelegate::elementContext() {
return HistoryView::Context::TTLViewer; return HistoryView::Context::TTLViewer;
} }
bool PreviewDelegate::elementIsChatWide() { HistoryView::ElementChatMode PreviewDelegate::elementChatMode() {
return _chatWide.current(); using Mode = HistoryView::ElementChatMode;
return _chatWide.current() ? Mode::Wide : Mode::Default;
} }
class PreviewWrap final : public Ui::RpWidget { class PreviewWrap final : public Ui::RpWidget {

View file

@ -940,27 +940,27 @@ void Widget::chosenRow(const ChosenRow &row) {
} }
} }
return; return;
} else if (history //} else if (history
&& history->peer->amMonoforumAdmin() // && history->peer->amMonoforumAdmin()
&& !row.message.fullId) { // && !row.message.fullId) {
const auto monoforum = history->peer->monoforum(); // const auto monoforum = history->peer->monoforum();
if (controller()->shownMonoforum().current() == monoforum) { // if (controller()->shownMonoforum().current() == monoforum) {
controller()->closeMonoforum(); // controller()->closeMonoforum();
//} else if (row.newWindow) { // #TODO monoforum // //} else if (row.newWindow) { // #TODO monoforum
// controller()->showInNewWindow( // // controller()->showInNewWindow(
// Window::SeparateId(Window::SeparateType::Forum, history)); // // Window::SeparateId(Window::SeparateType::Forum, history));
} else { // } else {
controller()->showMonoforum( // controller()->showMonoforum(
monoforum, // monoforum,
Window::SectionShow().withChildColumn()); // Window::SectionShow().withChildColumn());
if (!controller()->adaptive().isOneColumn()) { // if (!controller()->adaptive().isOneColumn()) {
controller()->showThread( // controller()->showThread(
history, // history,
ShowAtUnreadMsgId, // ShowAtUnreadMsgId,
Window::SectionShow::Way::ClearStack); // Window::SectionShow::Way::ClearStack);
} // }
} // }
return; // return;
} else if (history) { } else if (history) {
const auto peer = history->peer; const auto peer = history->peer;
const auto showAtMsgId = controller()->uniqueChatsInSearchResults() const auto showAtMsgId = controller()->uniqueChatsInSearchResults()
@ -999,13 +999,17 @@ void Widget::chosenRow(const ChosenRow &row) {
using namespace Window; using namespace Window;
auto params = SectionShow(SectionShow::Way::Forward); auto params = SectionShow(SectionShow::Way::Forward);
params.dropSameFromStack = true; params.dropSameFromStack = true;
using namespace HistoryView; params.highlightPart.text = _searchState.query;
controller()->showSection( if (!params.highlightPart.empty()) {
std::make_shared<ChatMemento>(ChatViewId{ params.highlightPartOffsetHint = kSearchQueryOffsetHint;
.history = sublist->owningHistory(), }
.sublist = sublist, if (false && row.newWindow) { // #TODO monoforum
}), controller()->showInNewWindow(
params); Window::SeparateId(sublist),
row.message.fullId.msg);
} else {
controller()->showThread(sublist, row.message.fullId.msg, params);
}
} }
if (row.filteredRow && !session().supportMode()) { if (row.filteredRow && !session().supportMode()) {
if (_subsectionTopBar) { if (_subsectionTopBar) {

View file

@ -152,7 +152,7 @@ void TopicsView::prepare(PeerId frontPeerId, Fn<void()> customEmojiRepaint) {
Ui::Text::SingleCustomEmoji( Ui::Text::SingleCustomEmoji(
manager->peerUserpicEmojiData(peer), manager->peerUserpicEmojiData(peer),
u"@"_q) u"@"_q)
).append(peer->shortName()); ).append(' ').append(peer->shortName());
title.key = key; title.key = key;
title.version = peer->nameVersion(); title.version = peer->nameVersion();
title.unread = unread; title.unread = unread;

View file

@ -740,8 +740,9 @@ void InnerWidget::elementSearchInList(
void InnerWidget::elementHandleViaClick(not_null<UserData*> bot) { void InnerWidget::elementHandleViaClick(not_null<UserData*> bot) {
} }
bool InnerWidget::elementIsChatWide() { HistoryView::ElementChatMode InnerWidget::elementChatMode() {
return _isChatWide; using Mode = HistoryView::ElementChatMode;
return _isChatWide ? Mode::Wide : Mode::Default;
} }
not_null<Ui::PathShiftGradient*> InnerWidget::elementPathShiftGradient() { not_null<Ui::PathShiftGradient*> InnerWidget::elementPathShiftGradient() {

View file

@ -131,7 +131,7 @@ public:
const QString &query, const QString &query,
const FullMsgId &context) override; const FullMsgId &context) override;
void elementHandleViaClick(not_null<UserData*> bot) override; void elementHandleViaClick(not_null<UserData*> bot) override;
bool elementIsChatWide() override; HistoryView::ElementChatMode elementChatMode() override;
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override; not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
void elementReplyTo(const FullReplyTo &to) override; void elementReplyTo(const FullReplyTo &to) override;
void elementStartInteraction( void elementStartInteraction(

View file

@ -241,8 +241,9 @@ public:
_widget->elementHandleViaClick(bot); _widget->elementHandleViaClick(bot);
} }
} }
bool elementIsChatWide() override { HistoryView::ElementChatMode elementChatMode() override {
return _widget ? _widget->elementIsChatWide() : false; using Mode = HistoryView::ElementChatMode;
return _widget ? _widget->elementChatMode() : Mode::Default;
} }
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override { not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override {
Expects(_widget != nullptr); Expects(_widget != nullptr);
@ -808,7 +809,11 @@ bool HistoryInner::canHaveFromUserpics() const {
} else if (const auto channel = _peer->asBroadcast()) { } else if (const auto channel = _peer->asBroadcast()) {
return channel->signatureProfiles(); return channel->signatureProfiles();
} }
return true; return !_removeFromUserpics;
}
void HistoryInner::toggleRemoveFromUserpics(bool remove) {
_removeFromUserpics = remove;
} }
template <typename Method> template <typename Method>
@ -3930,8 +3935,13 @@ void HistoryInner::elementHandleViaClick(not_null<UserData*> bot) {
_widget->insertBotCommand('@' + bot->username()); _widget->insertBotCommand('@' + bot->username());
} }
bool HistoryInner::elementIsChatWide() { HistoryView::ElementChatMode HistoryInner::elementChatMode() {
return _isChatWide; using Mode = HistoryView::ElementChatMode;
return _isChatWide
? Mode::Wide
: _removeFromUserpics
? Mode::Narrow
: Mode::Default;
} }
not_null<Ui::PathShiftGradient*> HistoryInner::elementPathShiftGradient() { not_null<Ui::PathShiftGradient*> HistoryInner::elementPathShiftGradient() {

View file

@ -35,6 +35,7 @@ struct SelectionModeResult;
struct StateRequest; struct StateRequest;
enum class CursorState : char; enum class CursorState : char;
enum class PointState : char; enum class PointState : char;
enum class ElementChatMode : char;
class EmptyPainter; class EmptyPainter;
class Element; class Element;
class TranslateTracker; class TranslateTracker;
@ -165,7 +166,7 @@ public:
const QString &query, const QString &query,
const FullMsgId &context); const FullMsgId &context);
void elementHandleViaClick(not_null<UserData*> bot); void elementHandleViaClick(not_null<UserData*> bot);
bool elementIsChatWide(); HistoryView::ElementChatMode elementChatMode();
not_null<Ui::PathShiftGradient*> elementPathShiftGradient(); not_null<Ui::PathShiftGradient*> elementPathShiftGradient();
void elementReplyTo(const FullReplyTo &to); void elementReplyTo(const FullReplyTo &to);
void elementStartInteraction(not_null<const Element*> view); void elementStartInteraction(not_null<const Element*> view);
@ -193,6 +194,7 @@ public:
void setChooseReportReason(Data::ReportInput reportInput); void setChooseReportReason(Data::ReportInput reportInput);
void clearChooseReportReason(); void clearChooseReportReason();
void toggleRemoveFromUserpics(bool remove);
// -1 if should not be visible, -2 if bad history() // -1 if should not be visible, -2 if bad history()
[[nodiscard]] int itemTop(const HistoryItem *item) const; [[nodiscard]] int itemTop(const HistoryItem *item) const;
@ -493,6 +495,7 @@ private:
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient; const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
QPainterPath _highlightPathCache; QPainterPath _highlightPathCache;
bool _isChatWide = false; bool _isChatWide = false;
bool _removeFromUserpics = false;
base::flat_set<not_null<const HistoryItem*>> _animatedStickersPlayed; base::flat_set<not_null<const HistoryItem*>> _animatedStickersPlayed;
base::flat_map<not_null<PeerData*>, Ui::PeerUserpicView> _userpics; base::flat_map<not_null<PeerData*>, Ui::PeerUserpicView> _userpics;

View file

@ -117,6 +117,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_reply.h" #include "history/view/history_view_reply.h"
#include "history/view/history_view_requests_bar.h" #include "history/view/history_view_requests_bar.h"
#include "history/view/history_view_sticker_toast.h" #include "history/view/history_view_sticker_toast.h"
#include "history/view/history_view_subsection_tabs.h"
#include "history/view/history_view_translate_bar.h" #include "history/view/history_view_translate_bar.h"
#include "history/view/media/history_view_media.h" #include "history/view/media/history_view_media.h"
#include "profile/profile_block_group_members.h" #include "profile/profile_block_group_members.h"
@ -236,6 +237,7 @@ HistoryWidget::HistoryWidget(
, _api(&controller->session().mtp()) , _api(&controller->session().mtp())
, _updateEditTimeLeftDisplay([=] { updateField(); }) , _updateEditTimeLeftDisplay([=] { updateField(); })
, _fieldBarCancel(this, st::historyReplyCancel) , _fieldBarCancel(this, st::historyReplyCancel)
, _topBars(std::make_unique<Ui::RpWidget>(this))
, _topBar(this, controller) , _topBar(this, controller)
, _scroll( , _scroll(
this, this,
@ -1713,6 +1715,7 @@ void HistoryWidget::applyInlineBotQuery(UserData *bot, const QString &query) {
void HistoryWidget::orderWidgets() { void HistoryWidget::orderWidgets() {
_voiceRecordBar->raise(); _voiceRecordBar->raise();
_send->raise(); _send->raise();
_topBars->raise();
if (_businessBotStatus) { if (_businessBotStatus) {
_businessBotStatus->bar().raise(); _businessBotStatus->bar().raise();
} }
@ -1740,6 +1743,9 @@ void HistoryWidget::orderWidgets() {
if (_chooseTheme) { if (_chooseTheme) {
_chooseTheme->raise(); _chooseTheme->raise();
} }
if (_subsectionTabs) {
_subsectionTabs->raise();
}
_topShadow->raise(); _topShadow->raise();
if (_autocomplete) { if (_autocomplete) {
_autocomplete->raise(); _autocomplete->raise();
@ -2467,6 +2473,11 @@ void HistoryWidget::showHistory(
_fieldDisabled = nullptr; _fieldDisabled = nullptr;
_silent.destroy(); _silent.destroy();
updateBotKeyboard(); updateBotKeyboard();
if (_subsectionTabs) {
_subsectionTabsLifetime.destroy();
controller()->saveSubsectionTabs(base::take(_subsectionTabs));
}
} else { } else {
Assert(_list == nullptr); Assert(_list == nullptr);
} }
@ -2501,7 +2512,7 @@ void HistoryWidget::showHistory(
_peer = session().data().peer(peerId); _peer = session().data().peer(peerId);
_contactStatus = std::make_unique<ContactStatus>( _contactStatus = std::make_unique<ContactStatus>(
controller(), controller(),
this, _topBars.get(),
_peer, _peer,
false); false);
_contactStatus->bar().heightValue( _contactStatus->bar().heightValue(
@ -2514,7 +2525,7 @@ void HistoryWidget::showHistory(
if (const auto user = _peer->asUser()) { if (const auto user = _peer->asUser()) {
_paysStatus = std::make_unique<PaysStatus>( _paysStatus = std::make_unique<PaysStatus>(
controller(), controller(),
this, _topBars.get(),
user); user);
_paysStatus->bar().heightValue( _paysStatus->bar().heightValue(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
@ -2522,7 +2533,7 @@ void HistoryWidget::showHistory(
}, _paysStatus->bar().lifetime()); }, _paysStatus->bar().lifetime());
_businessBotStatus = std::make_unique<BusinessBotStatus>( _businessBotStatus = std::make_unique<BusinessBotStatus>(
controller(), controller(),
this, _topBars.get(),
user); user);
_businessBotStatus->bar().heightValue( _businessBotStatus->bar().heightValue(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
@ -3194,29 +3205,12 @@ void HistoryWidget::updateControlsVisibility() {
} else if (!_firstLoadRequest && _scroll->isHidden()) { } else if (!_firstLoadRequest && _scroll->isHidden()) {
_scroll->show(); _scroll->show();
} }
if (_pinnedBar) { _topBars->show();
_pinnedBar->show();
}
if (_sponsoredMessageBar && checkSponsoredMessageBarVisibility()) { if (_sponsoredMessageBar && checkSponsoredMessageBarVisibility()) {
_sponsoredMessageBar->toggle(true, anim::type::normal); _sponsoredMessageBar->toggle(true, anim::type::normal);
} }
if (_translateBar) { if (_subsectionTabs) {
_translateBar->show(); _subsectionTabs->show();
}
if (_groupCallBar) {
_groupCallBar->show();
}
if (_requestsBar) {
_requestsBar->show();
}
if (_paysStatus) {
_paysStatus->show();
}
if (_contactStatus) {
_contactStatus->show();
}
if (_businessBotStatus) {
_businessBotStatus->show();
} }
if (isChoosingTheme() if (isChoosingTheme()
|| (!editingMessage() || (!editingMessage()
@ -4431,20 +4425,12 @@ void HistoryWidget::hideChildWidgets() {
if (_tabbedPanel) { if (_tabbedPanel) {
_tabbedPanel->hideFast(); _tabbedPanel->hideFast();
} }
if (_pinnedBar) {
_pinnedBar->hide();
}
if (_sponsoredMessageBar) { if (_sponsoredMessageBar) {
_sponsoredMessageBar->toggle(false, anim::type::instant); _sponsoredMessageBar->toggle(false, anim::type::instant);
} }
if (_translateBar) { _topBars->hide();
_translateBar->hide(); if (_subsectionTabs) {
} _subsectionTabs->hide();
if (_groupCallBar) {
_groupCallBar->hide();
}
if (_requestsBar) {
_requestsBar->hide();
} }
if (_voiceRecordBar) { if (_voiceRecordBar) {
_voiceRecordBar->hideFast(); _voiceRecordBar->hideFast();
@ -4455,15 +4441,6 @@ void HistoryWidget::hideChildWidgets() {
if (_chooseTheme) { if (_chooseTheme) {
_chooseTheme->hide(); _chooseTheme->hide();
} }
if (_paysStatus) {
_paysStatus->hide();
}
if (_contactStatus) {
_contactStatus->hide();
}
if (_businessBotStatus) {
_businessBotStatus->hide();
}
hideChildren(); hideChildren();
} }
@ -4747,6 +4724,8 @@ MsgId HistoryWidget::msgId() const {
void HistoryWidget::showAnimated( void HistoryWidget::showAnimated(
Window::SlideDirection direction, Window::SlideDirection direction,
const Window::SectionSlideParams &params) { const Window::SectionSlideParams &params) {
validateSubsectionTabs();
_showAnimation = nullptr; _showAnimation = nullptr;
// If we show pinned bar here, we don't want it to change the // If we show pinned bar here, we don't want it to change the
@ -4791,6 +4770,11 @@ void HistoryWidget::showAnimated(
activate(); activate();
} }
void HistoryWidget::showFast() {
validateSubsectionTabs();
show();
}
void HistoryWidget::showFinished() { void HistoryWidget::showFinished() {
_cornerButtons.finishAnimations(); _cornerButtons.finishAnimations();
if (_pinnedBar) { if (_pinnedBar) {
@ -6419,40 +6403,50 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) {
} }
void HistoryWidget::updateControlsGeometry() { void HistoryWidget::updateControlsGeometry() {
_topBar->resizeToWidth(width()); const auto width = this->width();
_topBar->resizeToWidth(width);
_topBar->moveToLeft(0, 0); _topBar->moveToLeft(0, 0);
_voiceRecordBar->resizeToWidth(width());
const auto tabsLeftSkip = _subsectionTabs
? _subsectionTabs->leftSkip()
: 0;
const auto innerWidth = width - tabsLeftSkip;
_voiceRecordBar->resizeToWidth(width);
moveFieldControls(); moveFieldControls();
const auto groupCallTop = _topBar->bottomNoMargins(); _topBars->move(tabsLeftSkip, _topBar->bottomNoMargins()
+ (_subsectionTabs ? _subsectionTabs->topSkip() : 0));
const auto groupCallTop = 0;
if (_groupCallBar) { if (_groupCallBar) {
_groupCallBar->move(0, groupCallTop); _groupCallBar->move(0, groupCallTop);
_groupCallBar->resizeToWidth(width()); _groupCallBar->resizeToWidth(innerWidth);
} }
const auto requestsTop = groupCallTop const auto requestsTop = groupCallTop
+ (_groupCallBar ? _groupCallBar->height() : 0); + (_groupCallBar ? _groupCallBar->height() : 0);
if (_requestsBar) { if (_requestsBar) {
_requestsBar->move(0, requestsTop); _requestsBar->move(0, requestsTop);
_requestsBar->resizeToWidth(width()); _requestsBar->resizeToWidth(innerWidth);
} }
const auto pinnedBarTop = requestsTop const auto pinnedBarTop = requestsTop
+ (_requestsBar ? _requestsBar->height() : 0); + (_requestsBar ? _requestsBar->height() : 0);
if (_pinnedBar) { if (_pinnedBar) {
_pinnedBar->move(0, pinnedBarTop); _pinnedBar->move(0, pinnedBarTop);
_pinnedBar->resizeToWidth(width()); _pinnedBar->resizeToWidth(innerWidth);
} }
const auto sponsoredMessageBarTop = pinnedBarTop const auto sponsoredMessageBarTop = pinnedBarTop
+ (_pinnedBar ? _pinnedBar->height() : 0); + (_pinnedBar ? _pinnedBar->height() : 0);
if (_sponsoredMessageBar) { if (_sponsoredMessageBar) {
_sponsoredMessageBar->move(0, sponsoredMessageBarTop); _sponsoredMessageBar->move(0, sponsoredMessageBarTop);
_sponsoredMessageBar->resizeToWidth(width()); _sponsoredMessageBar->resizeToWidth(innerWidth);
} }
const auto translateTop = sponsoredMessageBarTop const auto translateTop = sponsoredMessageBarTop
+ (_sponsoredMessageBar ? _sponsoredMessageBar->height() : 0); + (_sponsoredMessageBar ? _sponsoredMessageBar->height() : 0);
if (_translateBar) { if (_translateBar) {
_translateBar->move(0, translateTop); _translateBar->move(0, translateTop);
_translateBar->resizeToWidth(width()); _translateBar->resizeToWidth(innerWidth);
} }
const auto paysStatusTop = translateTop const auto paysStatusTop = translateTop
+ (_translateBar ? _translateBar->height() : 0); + (_translateBar ? _translateBar->height() : 0);
@ -6462,17 +6456,19 @@ void HistoryWidget::updateControlsGeometry() {
const auto contactStatusTop = paysStatusTop const auto contactStatusTop = paysStatusTop
+ (_paysStatus ? _paysStatus->bar().height() : 0); + (_paysStatus ? _paysStatus->bar().height() : 0);
if (_contactStatus) { if (_contactStatus) {
_contactStatus->bar().move(0, contactStatusTop); _contactStatus->bar().move(tabsLeftSkip, contactStatusTop);
} }
const auto businessBotTop = contactStatusTop const auto businessBotTop = contactStatusTop
+ (_contactStatus ? _contactStatus->bar().height() : 0); + (_contactStatus ? _contactStatus->bar().height() : 0);
if (_businessBotStatus) { if (_businessBotStatus) {
_businessBotStatus->bar().move(0, businessBotTop); _businessBotStatus->bar().move(tabsLeftSkip, businessBotTop);
} }
const auto scrollAreaTop = businessBotTop const auto scrollAreaTop = _topBars->y()
+ businessBotTop
+ (_businessBotStatus ? _businessBotStatus->bar().height() : 0); + (_businessBotStatus ? _businessBotStatus->bar().height() : 0);
_topBars->resize(innerWidth, scrollAreaTop - _topBars->y());
if (_scroll->y() != scrollAreaTop) { if (_scroll->y() != scrollAreaTop) {
_scroll->moveToLeft(0, scrollAreaTop); _scroll->moveToLeft(tabsLeftSkip, scrollAreaTop);
if (_autocomplete) { if (_autocomplete) {
_autocomplete->setBoundings(_scroll->geometry()); _autocomplete->setBoundings(_scroll->geometry());
} }
@ -6502,7 +6498,7 @@ void HistoryWidget::updateControlsGeometry() {
_topShadow->setGeometryToLeft( _topShadow->setGeometryToLeft(
topShadowLeft, topShadowLeft,
_topBar->bottomNoMargins(), _topBar->bottomNoMargins(),
width() - topShadowLeft - topShadowRight, width - topShadowLeft - topShadowRight,
st::lineWidth); st::lineWidth);
} }
@ -6704,7 +6700,12 @@ void HistoryWidget::updateHistoryGeometry(
return; return;
} }
auto newScrollHeight = height() - _topBar->height(); const auto newScrollWidth = width()
- (_subsectionTabs ? _subsectionTabs->leftSkip() : 0);
const auto subsectionTabsTop = _topBar->bottomNoMargins();
auto newScrollHeight = height()
- subsectionTabsTop
- (_subsectionTabs ? _subsectionTabs->topSkip() : 0);
if (_translateBar) { if (_translateBar) {
newScrollHeight -= _translateBar->height(); newScrollHeight -= _translateBar->height();
} }
@ -6760,10 +6761,10 @@ void HistoryWidget::updateHistoryGeometry(
} }
const auto wasScrollTop = _scroll->scrollTop(); const auto wasScrollTop = _scroll->scrollTop();
const auto wasAtBottom = (wasScrollTop == _scroll->scrollTopMax()); const auto wasAtBottom = (wasScrollTop == _scroll->scrollTopMax());
const auto needResize = (_scroll->width() != width()) const auto needResize = (_scroll->width() != newScrollWidth)
|| (_scroll->height() != newScrollHeight); || (_scroll->height() != newScrollHeight);
if (needResize) { if (needResize) {
_scroll->resize(width(), newScrollHeight); _scroll->resize(newScrollWidth, newScrollHeight);
// on initial updateListSize we didn't put the _scroll->scrollTop // on initial updateListSize we didn't put the _scroll->scrollTop
// correctly yet so visibleAreaUpdated() call will erase it // correctly yet so visibleAreaUpdated() call will erase it
// with the new (undefined) value // with the new (undefined) value
@ -6781,6 +6782,12 @@ void HistoryWidget::updateHistoryGeometry(
_cornerButtons.updatePositions(); _cornerButtons.updatePositions();
controller()->floatPlayerAreaUpdated(); controller()->floatPlayerAreaUpdated();
} }
if (_subsectionTabs) {
const auto scrollBottom = _scroll->y() + newScrollHeight;
const auto areaHeight = scrollBottom - subsectionTabsTop;
_subsectionTabs->setBoundingRect(
{ 0, subsectionTabsTop, width(), areaHeight });
}
updateListSize(); updateListSize();
_updateHistoryGeometryRequired = false; _updateHistoryGeometryRequired = false;
@ -7625,7 +7632,7 @@ void HistoryWidget::setupTranslateBar() {
Expects(_history != nullptr); Expects(_history != nullptr);
_translateBar = std::make_unique<HistoryView::TranslateBar>( _translateBar = std::make_unique<HistoryView::TranslateBar>(
this, _topBars.get(),
controller(), controller(),
_history); _history);
@ -7700,7 +7707,7 @@ void HistoryWidget::checkPinnedBarState() {
} }
clearHidingPinnedBar(); clearHidingPinnedBar();
_pinnedBar = std::make_unique<Ui::PinnedBar>(this, [=] { _pinnedBar = std::make_unique<Ui::PinnedBar>(_topBars.get(), [=] {
return controller()->isGifPausedAtLeastFor( return controller()->isGifPausedAtLeastFor(
Window::GifPauseReason::Any); Window::GifPauseReason::Any);
}, controller()->gifPauseLevelChanged()); }, controller()->gifPauseLevelChanged());
@ -7921,7 +7928,7 @@ void HistoryWidget::setupGroupCallBar() {
return; return;
} }
_groupCallBar = std::make_unique<Ui::GroupCallBar>( _groupCallBar = std::make_unique<Ui::GroupCallBar>(
this, _topBars.get(),
HistoryView::GroupCallBarContentByPeer( HistoryView::GroupCallBarContentByPeer(
peer, peer,
st::historyGroupCallUserpics.size, st::historyGroupCallUserpics.size,
@ -7974,7 +7981,7 @@ void HistoryWidget::setupRequestsBar() {
return; return;
} }
_requestsBar = std::make_unique<Ui::RequestsBar>( _requestsBar = std::make_unique<Ui::RequestsBar>(
this, _topBars.get(),
HistoryView::RequestsBarContentByPeer( HistoryView::RequestsBarContentByPeer(
peer, peer,
st::historyRequestsUserpics.size, st::historyRequestsUserpics.size,
@ -8087,7 +8094,7 @@ void HistoryWidget::checkSponsoredMessageBar() {
void HistoryWidget::createSponsoredMessageBar() { void HistoryWidget::createSponsoredMessageBar() {
_sponsoredMessageBar = base::make_unique_q<Ui::SlideWrap<>>( _sponsoredMessageBar = base::make_unique_q<Ui::SlideWrap<>>(
this, _topBars.get(),
object_ptr<Ui::RpWidget>(this)); object_ptr<Ui::RpWidget>(this));
_sponsoredMessageBar->entity()->resizeToWidth(_scroll->width()); _sponsoredMessageBar->entity()->resizeToWidth(_scroll->width());
@ -8250,6 +8257,34 @@ void HistoryWidget::showPremiumToast(not_null<DocumentData*> document) {
_stickerToast->showFor(document); _stickerToast->showFor(document);
} }
void HistoryWidget::validateSubsectionTabs() {
if (!_history || !HistoryView::SubsectionTabs::UsedFor(_history)) {
_subsectionTabsLifetime.destroy();
_subsectionTabs = nullptr;
return;
} else if (_subsectionTabs) {
return;
}
_subsectionTabs = controller()->restoreSubsectionTabsFor(this, _history);
if (!_subsectionTabs) {
_subsectionTabs = std::make_unique<HistoryView::SubsectionTabs>(
controller(),
this,
_history);
}
_subsectionTabs->removeRequests() | rpl::start_with_next([=] {
_subsectionTabs = nullptr;
updateControlsGeometry();
}, _subsectionTabsLifetime);
_subsectionTabs->layoutRequests() | rpl::start_with_next([=] {
_list->toggleRemoveFromUserpics(_subsectionTabs->leftSkip() > 0);
updateControlsGeometry();
orderWidgets();
}, _subsectionTabsLifetime);
updateControlsGeometry();
orderWidgets();
}
void HistoryWidget::checkCharsCount() { void HistoryWidget::checkCharsCount() {
_fieldCharsCountManager.setCount(Ui::ComputeFieldCharacterCount(_field)); _fieldCharsCountManager.setCount(Ui::ComputeFieldCharacterCount(_field));
checkCharsLimitation(); checkCharsLimitation();
@ -9439,5 +9474,7 @@ HistoryWidget::~HistoryWidget() {
session().data().itemVisibilitiesUpdated(); session().data().itemVisibilitiesUpdated();
} }
_subsectionTabsLifetime.destroy();
_subsectionTabs = nullptr;
setTabbedPanel(nullptr); setTabbedPanel(nullptr);
} }

View file

@ -107,6 +107,7 @@ class Element;
class PinnedTracker; class PinnedTracker;
class TranslateBar; class TranslateBar;
class ComposeSearch; class ComposeSearch;
class SubsectionTabs;
struct SelectedQuote; struct SelectedQuote;
} // namespace HistoryView } // namespace HistoryView
@ -183,6 +184,7 @@ public:
void showAnimated( void showAnimated(
Window::SlideDirection direction, Window::SlideDirection direction,
const Window::SectionSlideParams &params); const Window::SectionSlideParams &params);
void showFast();
void finishAnimating(); void finishAnimating();
void doneShow(); void doneShow();
@ -684,6 +686,8 @@ private:
void switchToSearch(QString query); void switchToSearch(QString query);
void validateSubsectionTabs();
void checkCharsCount(); void checkCharsCount();
void checkCharsLimitation(); void checkCharsLimitation();
@ -707,6 +711,8 @@ private:
object_ptr<Ui::IconButton> _fieldBarCancel; object_ptr<Ui::IconButton> _fieldBarCancel;
std::unique_ptr<Ui::RpWidget> _topBars;
std::unique_ptr<HistoryView::TranslateBar> _translateBar; std::unique_ptr<HistoryView::TranslateBar> _translateBar;
int _translateBarHeight = 0; int _translateBarHeight = 0;
@ -821,6 +827,8 @@ private:
const std::unique_ptr<VoiceRecordBar> _voiceRecordBar; const std::unique_ptr<VoiceRecordBar> _voiceRecordBar;
const std::unique_ptr<ForwardPanel> _forwardPanel; const std::unique_ptr<ForwardPanel> _forwardPanel;
std::unique_ptr<HistoryView::ComposeSearch> _composeSearch; std::unique_ptr<HistoryView::ComposeSearch> _composeSearch;
std::unique_ptr<HistoryView::SubsectionTabs> _subsectionTabs;
rpl::lifetime _subsectionTabsLifetime;
bool _cmdStartShown = false; bool _cmdStartShown = false;
object_ptr<Ui::InputField> _field; object_ptr<Ui::InputField> _field;
base::unique_qptr<Ui::RpWidget> _fieldDisabled; base::unique_qptr<Ui::RpWidget> _fieldDisabled;

View file

@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_contact_status.h" #include "history/view/history_view_contact_status.h"
#include "history/view/history_view_scheduled_section.h" #include "history/view/history_view_scheduled_section.h"
#include "history/view/history_view_service_message.h" #include "history/view/history_view_service_message.h"
#include "history/view/history_view_subsection_tabs.h"
#include "history/view/history_view_pinned_tracker.h" #include "history/view/history_view_pinned_tracker.h"
#include "history/view/history_view_pinned_section.h" #include "history/view/history_view_pinned_section.h"
#include "history/view/history_view_translate_bar.h" #include "history/view/history_view_translate_bar.h"
@ -237,6 +238,7 @@ ChatWidget::ChatWidget(
: nullptr) : nullptr)
, _topBar(this, controller) , _topBar(this, controller)
, _topBarShadow(this) , _topBarShadow(this)
, _topBars(std::make_unique<Ui::RpWidget>(this))
, _composeControls(std::make_unique<ComposeControls>( , _composeControls(std::make_unique<ComposeControls>(
this, this,
ComposeControlsDescriptor{ ComposeControlsDescriptor{
@ -256,7 +258,8 @@ ChatWidget::ChatWidget(
}) | rpl::type_erased() }) | rpl::type_erased()
: rpl::single(false), : rpl::single(false),
})) }))
, _translateBar(std::make_unique<TranslateBar>(this, controller, _history)) , _translateBar(
std::make_unique<TranslateBar>(_topBars.get(), controller, _history))
, _scroll(std::make_unique<Ui::ScrollArea>( , _scroll(std::make_unique<Ui::ScrollArea>(
this, this,
controller->chatStyle()->value(lifetime(), st::historyScroll), controller->chatStyle()->value(lifetime(), st::historyScroll),
@ -444,6 +447,10 @@ ChatWidget::~ChatWidget() {
if (_repliesRootId) { if (_repliesRootId) {
controller()->sendingAnimation().clear(); controller()->sendingAnimation().clear();
} }
if (_subsectionTabs) {
_subsectionTabsLifetime.destroy();
controller()->saveSubsectionTabs(base::take(_subsectionTabs));
}
if (_topic) { if (_topic) {
if (_topic->creating()) { if (_topic->creating()) {
_emptyPainter = nullptr; _emptyPainter = nullptr;
@ -471,9 +478,10 @@ void ChatWidget::orderWidgets() {
if (_pinnedBar) { if (_pinnedBar) {
_pinnedBar->raise(); _pinnedBar->raise();
} }
if (_topBar) { if (_subsectionTabs) {
_topBar->raise(); _subsectionTabs->raise();
} }
_topBar->raise();
_topBarShadow->raise(); _topBarShadow->raise();
_composeControls->raisePanels(); _composeControls->raisePanels();
} }
@ -499,7 +507,7 @@ void ChatWidget::setupRootView() {
if (_topic || !_repliesRootId) { if (_topic || !_repliesRootId) {
return; return;
} }
_repliesRootView = std::make_unique<Ui::PinnedBar>(this, [=] { _repliesRootView = std::make_unique<Ui::PinnedBar>(_topBars.get(), [=] {
return controller()->isGifPausedAtLeastFor( return controller()->isGifPausedAtLeastFor(
Window::GifPauseReason::Any); Window::GifPauseReason::Any);
}, controller()->gifPauseLevelChanged()); }, controller()->gifPauseLevelChanged());
@ -577,7 +585,9 @@ void ChatWidget::setupTopicViewer() {
void ChatWidget::subscribeToTopic() { void ChatWidget::subscribeToTopic() {
Expects(_topic != nullptr); Expects(_topic != nullptr);
_topicReopenBar = std::make_unique<TopicReopenBar>(this, _topic); _topicReopenBar = std::make_unique<TopicReopenBar>(
_topBars.get(),
_topic);
_topicReopenBar->bar().setVisible(!animatingShow()); _topicReopenBar->bar().setVisible(!animatingShow());
_topicReopenBarHeight = _topicReopenBar->bar().height(); _topicReopenBarHeight = _topicReopenBar->bar().height();
_topicReopenBar->bar().heightValue( _topicReopenBar->bar().heightValue(
@ -1509,6 +1519,37 @@ void ChatWidget::edit(
doSetInnerFocus(); doSetInnerFocus();
} }
void ChatWidget::validateSubsectionTabs() {
if (!HistoryView::SubsectionTabs::UsedFor(_history)) {
_subsectionTabsLifetime.destroy();
_subsectionTabs = nullptr;
return;
} else if (_subsectionTabs) {
return;
}
const auto thread = _topic ? (Data::Thread*)_topic : _sublist;
_subsectionTabs = controller()->restoreSubsectionTabsFor(this, thread);
if (!_subsectionTabs) {
_subsectionTabs = std::make_unique<HistoryView::SubsectionTabs>(
controller(),
this,
thread);
}
_subsectionTabs->removeRequests() | rpl::start_with_next([=] {
_subsectionTabs = nullptr;
updateControlsGeometry();
}, _subsectionTabsLifetime);
_subsectionTabs->layoutRequests() | rpl::start_with_next([=] {
_inner->overrideChatMode((_subsectionTabs->leftSkip() > 0)
? ElementChatMode::Narrow
: std::optional<ElementChatMode>());
updateControlsGeometry();
orderWidgets();
}, _subsectionTabsLifetime);
updateControlsGeometry();
orderWidgets();
}
void ChatWidget::refreshJoinGroupButton() { void ChatWidget::refreshJoinGroupButton() {
if (!_repliesRootId) { if (!_repliesRootId) {
return; return;
@ -1937,7 +1978,7 @@ void ChatWidget::checkPinnedBarState() {
} }
clearHidingPinnedBar(); clearHidingPinnedBar();
_pinnedBar = std::make_unique<Ui::PinnedBar>(this, [=] { _pinnedBar = std::make_unique<Ui::PinnedBar>(_topBars.get(), [=] {
return controller()->isGifPausedAtLeastFor( return controller()->isGifPausedAtLeastFor(
Window::GifPauseReason::Any); Window::GifPauseReason::Any);
}, controller()->gifPauseLevelChanged()); }, controller()->gifPauseLevelChanged());
@ -2252,13 +2293,10 @@ QPixmap ChatWidget::grabForShowAnimation(const Window::SectionSlideParams &param
if (params.withTopBarShadow) { if (params.withTopBarShadow) {
_topBarShadow->show(); _topBarShadow->show();
} }
if (_repliesRootView) { _topBars->hide();
_repliesRootView->hide(); if (_subsectionTabs) {
_subsectionTabs->hide();
} }
if (_pinnedBar) {
_pinnedBar->hide();
}
_translateBar->hide();
return result; return result;
} }
@ -2529,13 +2567,20 @@ void ChatWidget::updateControlsGeometry() {
: 0; : 0;
_topBar->resizeToWidth(contentWidth); _topBar->resizeToWidth(contentWidth);
_topBarShadow->resize(contentWidth, st::lineWidth); _topBarShadow->resize(contentWidth, st::lineWidth);
const auto tabsLeftSkip = _subsectionTabs
? _subsectionTabs->leftSkip()
: 0;
const auto innerWidth = contentWidth - tabsLeftSkip;
const auto subsectionTabsTop = _topBar->bottomNoMargins();
_topBars->move(tabsLeftSkip, subsectionTabsTop
+ (_subsectionTabs ? _subsectionTabs->topSkip() : 0));
if (_repliesRootView) { if (_repliesRootView) {
_repliesRootView->resizeToWidth(contentWidth); _repliesRootView->resizeToWidth(innerWidth);
} }
auto top = _topBar->height() + _repliesRootViewHeight; auto top = _repliesRootViewHeight;
if (_pinnedBar) { if (_pinnedBar) {
_pinnedBar->move(0, top); _pinnedBar->move(0, top);
_pinnedBar->resizeToWidth(contentWidth); _pinnedBar->resizeToWidth(innerWidth);
top += _pinnedBarHeight; top += _pinnedBarHeight;
} }
if (_topicReopenBar) { if (_topicReopenBar) {
@ -2543,7 +2588,7 @@ void ChatWidget::updateControlsGeometry() {
top += _topicReopenBar->bar().height(); top += _topicReopenBar->bar().height();
} }
_translateBar->move(0, top); _translateBar->move(0, top);
_translateBar->resizeToWidth(contentWidth); _translateBar->resizeToWidth(innerWidth);
top += _translateBarHeight; top += _translateBarHeight;
auto bottom = height(); auto bottom = height();
@ -2563,15 +2608,18 @@ void ChatWidget::updateControlsGeometry() {
bottom -= _composeControls->heightCurrent(); bottom -= _composeControls->heightCurrent();
} }
_topBars->resize(innerWidth, top);
top += _topBars->y();
const auto scrollHeight = bottom - top; const auto scrollHeight = bottom - top;
const auto scrollSize = QSize(contentWidth, scrollHeight); const auto scrollSize = QSize(innerWidth, scrollHeight);
if (_scroll->size() != scrollSize) { if (_scroll->size() != scrollSize) {
_skipScrollEvent = true; _skipScrollEvent = true;
_scroll->resize(scrollSize); _scroll->resize(scrollSize);
_inner->resizeToWidth(scrollSize.width(), _scroll->height()); _inner->resizeToWidth(scrollSize.width(), _scroll->height());
_skipScrollEvent = false; _skipScrollEvent = false;
} }
_scroll->move(0, top); _scroll->move(tabsLeftSkip, top);
if (!_scroll->isHidden()) { if (!_scroll->isHidden()) {
if (newScrollTop) { if (newScrollTop) {
_scroll->scrollToY(*newScrollTop); _scroll->scrollToY(*newScrollTop);
@ -2581,6 +2629,13 @@ void ChatWidget::updateControlsGeometry() {
_composeControls->move(0, bottom); _composeControls->move(0, bottom);
_composeControls->setAutocompleteBoundingRect(_scroll->geometry()); _composeControls->setAutocompleteBoundingRect(_scroll->geometry());
if (_subsectionTabs) {
const auto scrollBottom = _scroll->y() + scrollHeight;
const auto areaHeight = scrollBottom - subsectionTabsTop;
_subsectionTabs->setBoundingRect(
{ 0, subsectionTabsTop, width(), areaHeight });
}
_cornerButtons.updatePositions(); _cornerButtons.updatePositions();
} }
@ -2700,15 +2755,9 @@ void ChatWidget::showFinishedHook() {
_composeControls->showFinished(); _composeControls->showFinished();
} }
_inner->showFinished(); _inner->showFinished();
if (_repliesRootView) { _topBars->show();
_repliesRootView->show(); if (_subsectionTabs) {
} _subsectionTabs->show();
if (_pinnedBar) {
_pinnedBar->show();
}
_translateBar->show();
if (_topicReopenBar) {
_topicReopenBar->bar().show();
} }
// We should setup the drag area only after // We should setup the drag area only after

View file

@ -73,6 +73,7 @@ class TopicReopenBar;
class EmptyPainter; class EmptyPainter;
class PinnedTracker; class PinnedTracker;
class TranslateBar; class TranslateBar;
class SubsectionTabs;
struct ChatViewId { struct ChatViewId {
not_null<History*> history; not_null<History*> history;
@ -372,6 +373,7 @@ private:
Api::SendOptions options, Api::SendOptions options,
std::optional<MsgId> localMessageId); std::optional<MsgId> localMessageId);
void validateSubsectionTabs() override;
void setupEmptyPainter(); void setupEmptyPainter();
void refreshJoinGroupButton(); void refreshJoinGroupButton();
[[nodiscard]] bool emptyShown() const; [[nodiscard]] bool emptyShown() const;
@ -396,6 +398,7 @@ private:
QPointer<ListWidget> _inner; QPointer<ListWidget> _inner;
object_ptr<TopBarWidget> _topBar; object_ptr<TopBarWidget> _topBar;
object_ptr<Ui::PlainShadow> _topBarShadow; object_ptr<Ui::PlainShadow> _topBarShadow;
std::unique_ptr<Ui::RpWidget> _topBars;
std::unique_ptr<ComposeControls> _composeControls; std::unique_ptr<ComposeControls> _composeControls;
std::unique_ptr<ComposeSearch> _composeSearch; std::unique_ptr<ComposeSearch> _composeSearch;
std::unique_ptr<Ui::FlatButton> _joinGroup; std::unique_ptr<Ui::FlatButton> _joinGroup;
@ -404,6 +407,8 @@ private:
std::unique_ptr<Ui::FlatButton> _openChatButton; std::unique_ptr<Ui::FlatButton> _openChatButton;
std::unique_ptr<Ui::RpWidget> _aboutHiddenAuthor; std::unique_ptr<Ui::RpWidget> _aboutHiddenAuthor;
std::unique_ptr<EmptyPainter> _emptyPainter; std::unique_ptr<EmptyPainter> _emptyPainter;
std::unique_ptr<SubsectionTabs> _subsectionTabs;
rpl::lifetime _subsectionTabsLifetime;
bool _canSendTexts = false; bool _canSendTexts = false;
bool _skipScrollEvent = false; bool _skipScrollEvent = false;
bool _synteticScrollEvent = false; bool _synteticScrollEvent = false;

View file

@ -262,8 +262,8 @@ void DefaultElementDelegate::elementHandleViaClick(
not_null<UserData*> bot) { not_null<UserData*> bot) {
} }
bool DefaultElementDelegate::elementIsChatWide() { ElementChatMode DefaultElementDelegate::elementChatMode() {
return false; return ElementChatMode::Default;
} }
void DefaultElementDelegate::elementReplyTo(const FullReplyTo &to) { void DefaultElementDelegate::elementReplyTo(const FullReplyTo &to) {
@ -410,7 +410,7 @@ void UnreadBar::paint(
const PaintContext &context, const PaintContext &context,
int y, int y,
int w, int w,
bool chatWide) const { ElementChatMode mode) const {
const auto previousTranslation = p.transform().dx(); const auto previousTranslation = p.transform().dx();
if (previousTranslation != 0) { if (previousTranslation != 0) {
p.translate(-previousTranslation, 0); p.translate(-previousTranslation, 0);
@ -434,7 +434,7 @@ void UnreadBar::paint(
p.setPen(st->historyUnreadBarFg()); p.setPen(st->historyUnreadBarFg());
int maxwidth = w; int maxwidth = w;
if (chatWide) { if (mode == ElementChatMode::Wide) {
maxwidth = qMin( maxwidth = qMin(
maxwidth, maxwidth,
st::msgMaxWidth st::msgMaxWidth
@ -609,9 +609,9 @@ void ServicePreMessage::init(PreparedServiceText string) {
} }
} }
int ServicePreMessage::resizeToWidth(int newWidth, bool chatWide) { int ServicePreMessage::resizeToWidth(int newWidth, ElementChatMode mode) {
width = newWidth; width = newWidth;
if (chatWide) { if (mode == ElementChatMode::Wide) {
accumulate_min( accumulate_min(
width, width,
st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left()); st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left());
@ -644,7 +644,7 @@ void ServicePreMessage::paint(
Painter &p, Painter &p,
const PaintContext &context, const PaintContext &context,
QRect g, QRect g,
bool chatWide) const { ElementChatMode mode) const {
const auto top = g.top() - height - st::msgMargin.top(); const auto top = g.top() - height - st::msgMargin.top();
p.translate(0, top); p.translate(0, top);
@ -987,7 +987,8 @@ not_null<PurchasedTag*> Element::enforcePurchasedTag() {
int Element::AdditionalSpaceForSelectionCheckbox( int Element::AdditionalSpaceForSelectionCheckbox(
not_null<const Element*> view, not_null<const Element*> view,
QRect countedGeometry) { QRect countedGeometry) {
if (!view->hasOutLayout() || view->delegate()->elementIsChatWide()) { if (!view->hasOutLayout()
|| view->delegate()->elementChatMode() == ElementChatMode::Wide) {
return 0; return 0;
} }
if (countedGeometry.isEmpty()) { if (countedGeometry.isEmpty()) {
@ -1698,7 +1699,8 @@ bool Element::hasOutLayout() const {
} }
bool Element::hasRightLayout() const { bool Element::hasRightLayout() const {
return hasOutLayout() && !_delegate->elementIsChatWide(); return hasOutLayout()
&& (_delegate->elementChatMode() != ElementChatMode::Wide);
} }
bool Element::drawBubble() const { bool Element::drawBubble() const {

View file

@ -78,6 +78,12 @@ struct SelectionModeResult {
float64 progress = 0.0; float64 progress = 0.0;
}; };
enum class ElementChatMode : char {
Default,
Wide,
Narrow, // monoforum with left tabs
};
class Element; class Element;
class ElementDelegate { class ElementDelegate {
public: public:
@ -114,7 +120,7 @@ public:
const QString &query, const QString &query,
const FullMsgId &context) = 0; const FullMsgId &context) = 0;
virtual void elementHandleViaClick(not_null<UserData*> bot) = 0; virtual void elementHandleViaClick(not_null<UserData*> bot) = 0;
virtual bool elementIsChatWide() = 0; virtual ElementChatMode elementChatMode() = 0;
virtual not_null<Ui::PathShiftGradient*> elementPathShiftGradient() = 0; virtual not_null<Ui::PathShiftGradient*> elementPathShiftGradient() = 0;
virtual void elementReplyTo(const FullReplyTo &to) = 0; virtual void elementReplyTo(const FullReplyTo &to) = 0;
virtual void elementStartInteraction(not_null<const Element*> view) = 0; virtual void elementStartInteraction(not_null<const Element*> view) = 0;
@ -169,7 +175,7 @@ public:
const QString &query, const QString &query,
const FullMsgId &context) override; const FullMsgId &context) override;
void elementHandleViaClick(not_null<UserData*> bot) override; void elementHandleViaClick(not_null<UserData*> bot) override;
bool elementIsChatWide() override; ElementChatMode elementChatMode() override;
void elementReplyTo(const FullReplyTo &to) override; void elementReplyTo(const FullReplyTo &to) override;
void elementStartInteraction(not_null<const Element*> view) override; void elementStartInteraction(not_null<const Element*> view) override;
void elementStartPremium( void elementStartPremium(
@ -233,7 +239,7 @@ struct UnreadBar : RuntimeComponent<UnreadBar, Element> {
const PaintContext &context, const PaintContext &context,
int y, int y,
int w, int w,
bool chatWide) const; ElementChatMode mode) const;
QString text; QString text;
int width = 0; int width = 0;
@ -305,13 +311,13 @@ private:
struct ServicePreMessage : RuntimeComponent<ServicePreMessage, Element> { struct ServicePreMessage : RuntimeComponent<ServicePreMessage, Element> {
void init(PreparedServiceText string); void init(PreparedServiceText string);
int resizeToWidth(int newWidth, bool chatWide); int resizeToWidth(int newWidth, ElementChatMode mode);
void paint( void paint(
Painter &p, Painter &p,
const PaintContext &context, const PaintContext &context,
QRect g, QRect g,
bool chatWide) const; ElementChatMode mode) const;
[[nodiscard]] ClickHandlerPtr textState( [[nodiscard]] ClickHandlerPtr textState(
QPoint point, QPoint point,
const StateRequest &request, const StateRequest &request,

View file

@ -1898,8 +1898,10 @@ void ListWidget::elementHandleViaClick(not_null<UserData*> bot) {
_delegate->listHandleViaClick(bot); _delegate->listHandleViaClick(bot);
} }
bool ListWidget::elementIsChatWide() { ElementChatMode ListWidget::elementChatMode() {
return _overrideIsChatWide.value_or(_isChatWide); return _overrideChatMode.value_or(_isChatWide
? ElementChatMode::Wide
: ElementChatMode::Default);
} }
not_null<Ui::PathShiftGradient*> ListWidget::elementPathShiftGradient() { not_null<Ui::PathShiftGradient*> ListWidget::elementPathShiftGradient() {
@ -4284,8 +4286,8 @@ void ListWidget::setEmptyInfoWidget(base::unique_qptr<Ui::RpWidget> &&w) {
} }
} }
void ListWidget::overrideIsChatWide(bool isWide) { void ListWidget::overrideChatMode(std::optional<ElementChatMode> mode) {
_overrideIsChatWide = isWide; _overrideChatMode = mode;
} }
ListWidget::~ListWidget() { ListWidget::~ListWidget() {

View file

@ -428,7 +428,7 @@ public:
const QString &query, const QString &query,
const FullMsgId &context) override; const FullMsgId &context) override;
void elementHandleViaClick(not_null<UserData*> bot) override; void elementHandleViaClick(not_null<UserData*> bot) override;
bool elementIsChatWide() override; ElementChatMode elementChatMode() override;
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override; not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
void elementReplyTo(const FullReplyTo &to) override; void elementReplyTo(const FullReplyTo &to) override;
void elementStartInteraction(not_null<const Element*> view) override; void elementStartInteraction(not_null<const Element*> view) override;
@ -443,7 +443,7 @@ public:
bool elementHideTopicButton(not_null<const Element*> view) override; bool elementHideTopicButton(not_null<const Element*> view) override;
void setEmptyInfoWidget(base::unique_qptr<Ui::RpWidget> &&w); void setEmptyInfoWidget(base::unique_qptr<Ui::RpWidget> &&w);
void overrideIsChatWide(bool isWide); void overrideChatMode(std::optional<ElementChatMode> mode);
~ListWidget(); ~ListWidget();
@ -834,7 +834,7 @@ private:
bool _refreshingViewer = false; bool _refreshingViewer = false;
bool _showFinished = false; bool _showFinished = false;
bool _resizePending = false; bool _resizePending = false;
std::optional<bool> _overrideIsChatWide; std::optional<ElementChatMode> _overrideChatMode;
// _menu must be destroyed before _whoReactedMenuLifetime. // _menu must be destroyed before _whoReactedMenuLifetime.
rpl::lifetime _whoReactedMenuLifetime; rpl::lifetime _whoReactedMenuLifetime;

View file

@ -1142,18 +1142,13 @@ void Message::draw(Painter &p, const PaintContext &context) const {
} }
if (context.clip.intersects(QRect(0, aboveh, width(), unreadbarh))) { if (context.clip.intersects(QRect(0, aboveh, width(), unreadbarh))) {
p.translate(0, aboveh); p.translate(0, aboveh);
bar->paint( bar->paint(p, context, 0, width(), delegate()->elementChatMode());
p,
context,
0,
width(),
delegate()->elementIsChatWide());
p.translate(0, -aboveh); p.translate(0, -aboveh);
} }
} }
if (const auto service = Get<ServicePreMessage>()) { if (const auto service = Get<ServicePreMessage>()) {
service->paint(p, context, g, delegate()->elementIsChatWide()); service->paint(p, context, g, delegate()->elementChatMode());
} }
if (isHidden()) { if (isHidden()) {
@ -1549,8 +1544,8 @@ void Message::draw(Painter &p, const PaintContext &context) const {
constexpr auto kMaxHeightRatio = 3.5; constexpr auto kMaxHeightRatio = 3.5;
constexpr auto kStrokeWidth = 2.; constexpr auto kStrokeWidth = 2.;
constexpr auto kWaveWidth = 10.; constexpr auto kWaveWidth = 10.;
const auto isLeftSize = (!context.outbg) const auto isLeftSize = !context.outbg
|| delegate()->elementIsChatWide(); || (delegate()->elementChatMode() == ElementChatMode::Wide);
const auto ratio = std::min(context.gestureHorizontal.ratio, 1.); const auto ratio = std::min(context.gestureHorizontal.ratio, 1.);
const auto reachRatio = context.gestureHorizontal.reachRatio; const auto reachRatio = context.gestureHorizontal.reachRatio;
const auto size = st::historyFastShareSize; const auto size = st::historyFastShareSize;
@ -1635,7 +1630,8 @@ void Message::draw(Painter &p, const PaintContext &context) const {
} }
const auto o = ScopedPainterOpacity(p, progress); const auto o = ScopedPainterOpacity(p, progress);
const auto &st = st::msgSelectionCheck; const auto &st = st::msgSelectionCheck;
const auto right = delegate()->elementIsChatWide() const auto right = (delegate()->elementChatMode()
== ElementChatMode::Wide)
? std::min( ? std::min(
int(_bubbleWidthLimit int(_bubbleWidthLimit
+ st::msgPhotoSkip + st::msgPhotoSkip
@ -2465,7 +2461,7 @@ bool Message::hasFromPhoto() const {
case Context::AdminLog: case Context::AdminLog:
return true; return true;
case Context::Monoforum: case Context::Monoforum:
return delegate()->elementIsChatWide(); return (delegate()->elementChatMode() == ElementChatMode::Wide);
case Context::History: case Context::History:
case Context::ChatPreview: case Context::ChatPreview:
case Context::TTLViewer: case Context::TTLViewer:
@ -2484,8 +2480,10 @@ bool Message::hasFromPhoto() const {
|| item->isFakeAboutView() || item->isFakeAboutView()
|| (context() == Context::Replies && item->isDiscussionPost())) { || (context() == Context::Replies && item->isDiscussionPost())) {
return false; return false;
} else if (delegate()->elementIsChatWide()) { }
return true; const auto mode = delegate()->elementChatMode();
if (mode != ElementChatMode::Default) {
return (mode == ElementChatMode::Wide);
} else if (item->history()->peer->isVerifyCodes()) { } else if (item->history()->peer->isVerifyCodes()) {
return !hasOutLayout(); return !hasOutLayout();
} else if (item->Has<HistoryMessageForwarded>()) { } else if (item->Has<HistoryMessageForwarded>()) {
@ -4385,12 +4383,15 @@ QRect Message::countGeometry() const {
? media->width() ? media->width()
: width(); : width();
const auto outbg = hasOutLayout(); const auto outbg = hasOutLayout();
const auto useMoreSpace = (delegate()->elementChatMode()
== ElementChatMode::Narrow);
const auto wideSkip = useMoreSpace
? st::msgMargin.left()
: st::msgMargin.right();
const auto availableWidth = width() const auto availableWidth = width()
- st::msgMargin.left() - st::msgMargin.left()
- (centeredView ? st::msgMargin.left() : st::msgMargin.right()); - (centeredView ? st::msgMargin.left() : wideSkip);
auto contentLeft = hasRightLayout() auto contentLeft = hasRightLayout() ? wideSkip : st::msgMargin.left();
? st::msgMargin.right()
: st::msgMargin.left();
auto contentWidth = availableWidth; auto contentWidth = availableWidth;
if (hasFromPhoto()) { if (hasFromPhoto()) {
contentLeft += st::msgPhotoSkip; contentLeft += st::msgPhotoSkip;
@ -4411,7 +4412,8 @@ QRect Message::countGeometry() const {
contentWidth = mediaWidth; contentWidth = mediaWidth;
} }
} }
if (contentWidth < availableWidth && !delegate()->elementIsChatWide()) { if (contentWidth < availableWidth
&& delegate()->elementChatMode() != ElementChatMode::Wide) {
if (outbg) { if (outbg) {
contentLeft += availableWidth - contentWidth; contentLeft += availableWidth - contentWidth;
} else if (centeredView) { } else if (centeredView) {
@ -4500,7 +4502,7 @@ int Message::resizeContentGetHeight(int newWidth) {
auto newHeight = minHeight(); auto newHeight = minHeight();
if (const auto service = Get<ServicePreMessage>()) { if (const auto service = Get<ServicePreMessage>()) {
service->resizeToWidth(newWidth, delegate()->elementIsChatWide()); service->resizeToWidth(newWidth, delegate()->elementChatMode());
} }
const auto botTop = item->isFakeAboutView() const auto botTop = item->isFakeAboutView()
@ -4515,9 +4517,14 @@ int Message::resizeContentGetHeight(int newWidth) {
// This code duplicates countGeometry() but also resizes media. // This code duplicates countGeometry() but also resizes media.
const auto centeredView = item->isFakeAboutView() const auto centeredView = item->isFakeAboutView()
|| (context() == Context::Replies && item->isDiscussionPost()); || (context() == Context::Replies && item->isDiscussionPost());
const auto useMoreSpace = (delegate()->elementChatMode()
== ElementChatMode::Narrow);
const auto wideSkip = useMoreSpace
? st::msgMargin.left()
: st::msgMargin.right();
auto contentWidth = newWidth auto contentWidth = newWidth
- st::msgMargin.left() - st::msgMargin.left()
- (centeredView ? st::msgMargin.left() : st::msgMargin.right()); - (centeredView ? st::msgMargin.left() : wideSkip);
if (hasFromPhoto()) { if (hasFromPhoto()) {
if (const auto size = rightActionSize()) { if (const auto size = rightActionSize()) {
contentWidth -= size->width() + (st::msgPhotoSkip - st::historyFastShareSize); contentWidth -= size->width() + (st::msgPhotoSkip - st::historyFastShareSize);

View file

@ -423,7 +423,7 @@ bool Service::consumeHorizontalScroll(QPoint position, int delta) {
QRect Service::countGeometry() const { QRect Service::countGeometry() const {
auto result = QRect(0, 0, width(), height()); auto result = QRect(0, 0, width(), height());
if (delegate()->elementIsChatWide()) { if (delegate()->elementChatMode() == ElementChatMode::Wide) {
result.setWidth(qMin(result.width(), st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left())); result.setWidth(qMin(result.width(), st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left()));
} }
auto margins = st::msgServiceMargin; auto margins = st::msgServiceMargin;
@ -469,7 +469,7 @@ QSize Service::performCountCurrentSize(int newWidth) {
+ media->resizeGetHeight(newWidth) + media->resizeGetHeight(newWidth)
+ st::msgServiceMargin.bottom(); + st::msgServiceMargin.bottom();
} else if (!text().isEmpty()) { } else if (!text().isEmpty()) {
if (delegate()->elementIsChatWide()) { if (delegate()->elementChatMode() == ElementChatMode::Wide) {
accumulate_min(contentWidth, st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left()); accumulate_min(contentWidth, st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left());
} }
contentWidth -= st::msgServiceMargin.left() + st::msgServiceMargin.left(); // two small margins contentWidth -= st::msgServiceMargin.left() + st::msgServiceMargin.left(); // two small margins
@ -561,7 +561,7 @@ void Service::draw(Painter &p, const PaintContext &context) const {
context, context,
0, 0,
width(), width(),
delegate()->elementIsChatWide()); delegate()->elementChatMode());
p.translate(0, -aboveh); p.translate(0, -aboveh);
} }
} }

View file

@ -0,0 +1,384 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "history/view/history_view_subsection_tabs.h"
#include "core/ui_integration.h"
#include "data/stickers/data_custom_emoji.h"
#include "data/data_channel.h"
#include "data/data_forum.h"
#include "data/data_forum_topic.h"
#include "data/data_saved_messages.h"
#include "data/data_saved_sublist.h"
#include "data/data_session.h"
#include "data/data_thread.h"
#include "dialogs/dialogs_main_list.h"
#include "history/history.h"
#include "lang/lang_keys.h"
#include "ui/text/text_utilities.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/discrete_sliders.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/shadow.h"
#include "window/window_session_controller.h"
#include "styles/style_chat.h"
namespace HistoryView {
namespace {
constexpr auto kDefaultLimit = 10;
} // namespace
SubsectionTabs::SubsectionTabs(
not_null<Window::SessionController*> controller,
not_null<Ui::RpWidget*> parent,
not_null<Data::Thread*> thread)
: _controller(controller)
, _history(thread->owningHistory())
, _active(thread)
, _around(thread)
, _beforeLimit(kDefaultLimit)
, _afterLimit(kDefaultLimit) {
track();
refreshSlice();
setupHorizontal(parent);
}
SubsectionTabs::~SubsectionTabs() {
delete base::take(_horizontal);
delete base::take(_vertical);
delete base::take(_shadow);
}
void SubsectionTabs::setupHorizontal(not_null<QWidget*> parent) {
delete base::take(_vertical);
_horizontal = Ui::CreateChild<Ui::RpWidget>(parent);
_horizontal->show();
if (!_shadow) {
_shadow = Ui::CreateChild<Ui::PlainShadow>(parent);
_shadow->show();
}
const auto toggle = Ui::CreateChild<Ui::IconButton>(
_horizontal,
st::chatTabsToggle);
toggle->show();
toggle->setClickedCallback([=] {
toggleModes();
});
toggle->move(0, 0);
const auto scroll = Ui::CreateChild<Ui::ScrollArea>(
_horizontal,
st::chatTabsScroll,
true);
scroll->show();
const auto tabs = scroll->setOwnedWidget(
object_ptr<Ui::SettingsSlider>(scroll, st::chatTabsSlider));
tabs->sectionActivated() | rpl::start_with_next([=](int active) {
if (active >= 0
&& active < _slice.size()
&& _active != _slice[active]) {
auto params = Window::SectionShow();
params.way = Window::SectionShow::Way::ClearStack;
params.animated = anim::type::instant;
_controller->showThread(_slice[active], {}, params);
}
}, tabs->lifetime());
_horizontal->sizeValue(
) | rpl::start_with_next([=](QSize size) {
const auto togglew = toggle->width();
const auto height = size.height();
scroll->setGeometry(togglew, 0, size.width() - togglew, height);
}, scroll->lifetime());
_horizontal->paintRequest() | rpl::start_with_next([=](QRect clip) {
QPainter(_horizontal).fillRect(clip, st::windowBg);
}, _horizontal->lifetime());
_refreshed.events_starting_with_copy(
rpl::empty
) | rpl::start_with_next([=] {
auto sections = std::vector<TextWithEntities>();
const auto manager = &_history->owner().customEmojiManager();
auto activeIndex = -1;
for (const auto &thread : _slice) {
if (thread == _active) {
activeIndex = int(sections.size());
}
if (const auto topic = thread->asTopic()) {
sections.push_back(topic->titleWithIcon());
} else if (const auto sublist = thread->asSublist()) {
const auto peer = sublist->sublistPeer();
sections.push_back(TextWithEntities().append(
Ui::Text::SingleCustomEmoji(
manager->peerUserpicEmojiData(peer),
u"@"_q)
).append(' ').append(peer->shortName()));
} else {
sections.push_back(tr::lng_filters_all_short(
tr::now,
Ui::Text::WithEntities));
}
}
tabs->setSections(sections, Core::TextContext({
.session = &_history->session(),
}));
tabs->fitWidthToSections();
tabs->setActiveSectionFast(activeIndex);
_horizontal->resize(
tabs->width(),
std::max(toggle->height(), tabs->height()));
}, _horizontal->lifetime());
}
void SubsectionTabs::setupVertical(not_null<QWidget*> parent) {
delete base::take(_horizontal);
_vertical = Ui::CreateChild<Ui::RpWidget>(parent);
_vertical->show();
if (!_shadow) {
_shadow = Ui::CreateChild<Ui::PlainShadow>(parent);
_shadow->show();
}
const auto toggle = Ui::CreateChild<Ui::IconButton>(
_vertical,
st::chatTabsToggle);
toggle->show();
const auto active = &st::chatTabsToggleActive;
toggle->setIconOverride(active, active);
toggle->setClickedCallback([=] {
toggleModes();
});
toggle->move(0, 0);
const auto scroll = Ui::CreateChild<Ui::ScrollArea>(_vertical);
scroll->show();
_vertical->sizeValue(
) | rpl::start_with_next([=](QSize size) {
const auto toggleh = toggle->height();
const auto width = size.width();
scroll->setGeometry(0, toggleh, width, size.height() - toggleh);
}, scroll->lifetime());
_vertical->paintRequest() | rpl::start_with_next([=](QRect clip) {
QPainter(_vertical).fillRect(clip, st::windowBg);
}, _vertical->lifetime());
_refreshed.events_starting_with_copy(
rpl::empty
) | rpl::start_with_next([=] {
_vertical->resize(std::max(toggle->width(), 0), 0);
}, _vertical->lifetime());
}
void SubsectionTabs::toggleModes() {
Expects((_horizontal || _vertical) && _shadow);
if (_horizontal) {
setupVertical(_horizontal->parentWidget());
} else {
setupHorizontal(_vertical->parentWidget());
}
_layoutRequests.fire({});
}
rpl::producer<> SubsectionTabs::removeRequests() const {
if (const auto forum = _history->peer->forum()) {
return forum->destroyed();
} else if (const auto monoforum = _history->peer->monoforum()) {
return monoforum->destroyed();
} else {
Unexpected("Peer in SubsectionTabs::removeRequests.");
}
}
void SubsectionTabs::extractToParent(not_null<Ui::RpWidget*> parent) {
Expects((_horizontal || _vertical) && _shadow);
if (_vertical) {
_vertical->hide();
_vertical->setParent(parent);
} else {
_horizontal->hide();
_horizontal->setParent(parent);
}
_shadow->hide();
_shadow->setParent(parent);
}
void SubsectionTabs::setBoundingRect(QRect boundingRect) {
Expects((_horizontal || _vertical) && _shadow);
if (_horizontal) {
_horizontal->setGeometry(
boundingRect.x(),
boundingRect.y(),
boundingRect.width(),
_horizontal->height());
_shadow->setGeometry(
boundingRect.x(),
_horizontal->y() + _horizontal->height(),
boundingRect.width(),
st::lineWidth);
} else {
_vertical->setGeometry(
boundingRect.x(),
boundingRect.y(),
_vertical->width(),
boundingRect.height());
_shadow->setGeometry(
_vertical->x() + _vertical->width(),
boundingRect.y(),
st::lineWidth,
boundingRect.height());
}
}
rpl::producer<> SubsectionTabs::layoutRequests() const {
return _layoutRequests.events();
}
int SubsectionTabs::leftSkip() const {
return _vertical ? _vertical->width() : 0;
}
int SubsectionTabs::topSkip() const {
return _horizontal ? _horizontal->height() : 0;
}
void SubsectionTabs::raise() {
Expects((_horizontal || _vertical) && _shadow);
if (_horizontal) {
_horizontal->raise();
} else {
_vertical->raise();
}
_shadow->raise();
}
void SubsectionTabs::show() {
setVisible(true);
}
void SubsectionTabs::hide() {
setVisible(false);
}
void SubsectionTabs::setVisible(bool shown) {
Expects((_horizontal || _vertical) && _shadow);
if (_horizontal) {
_horizontal->setVisible(shown);
} else {
_vertical->setVisible(shown);
}
_shadow->setVisible(shown);
}
void SubsectionTabs::track() {
if (const auto forum = _history->peer->forum()) {
forum->topicDestroyed(
) | rpl::start_with_next([=](not_null<Data::ForumTopic*> topic) {
if (_around == topic) {
_around = _history;
refreshSlice();
}
}, _lifetime);
} else if (const auto monoforum = _history->peer->monoforum()) {
monoforum->sublistDestroyed(
) | rpl::start_with_next([=](not_null<Data::SavedSublist*> sublist) {
if (_around == sublist) {
_around = _history;
refreshSlice();
}
}, _lifetime);
} else {
Unexpected("Peer in SubsectionTabs::track.");
}
}
void SubsectionTabs::refreshSlice() {
const auto forum = _history->peer->forum();
const auto monoforum = _history->peer->monoforum();
Assert(forum || monoforum);
const auto list = forum
? forum->topicsList()
: monoforum->chatsList();
auto slice = std::vector<not_null<Data::Thread*>>();
const auto guard = gsl::finally([&] {
if (_slice != slice) {
_slice = std::move(slice);
_refreshed.fire({});
}
});
if (!list) {
slice.push_back(_history);
return;
}
const auto &chats = list->indexed()->all();
auto i = (_around == _history)
? chats.end()
: ranges::find(chats, _around, [](not_null<Dialogs::Row*> row) {
return not_null(row->thread());
});
if (i == chats.end()) {
i = chats.begin();
}
const auto takeBefore = std::min(_beforeLimit, int(i - chats.begin()));
const auto takeAfter = std::min(_afterLimit, int(chats.end() - i));
const auto from = i - takeBefore;
const auto till = i + takeAfter;
_beforeSkipped = std::max(0, int(from - chats.begin()));
_afterSkipped = list->loaded()
? std::max(0, int(chats.end() - till))
: std::optional<int>();
if (from == chats.begin()) {
slice.push_back(_history);
}
for (auto i = from; i != till; ++i) {
slice.push_back((*i)->thread());
}
}
bool SubsectionTabs::switchTo(
not_null<Data::Thread*> thread,
not_null<Ui::RpWidget*> parent) {
Expects((_horizontal || _vertical) && _shadow);
if (thread->owningHistory() != _history) {
return false;
}
if (_vertical) {
_vertical->setParent(parent);
_vertical->show();
} else {
_horizontal->setParent(parent);
_horizontal->show();
}
_shadow->setParent(parent);
_shadow->show();
return true;
}
bool SubsectionTabs::UsedFor(not_null<Data::Thread*> thread) {
const auto history = thread->owningHistory();
if (history->amMonoforumAdmin()) {
return true;
}
const auto channel = history->peer->asChannel();
return channel
&& channel->isForum()
&& ((channel->flags() & ChannelDataFlag::ForumTabs) || true); AssertIsDebug();
}
} // namespace HistoryView

View file

@ -0,0 +1,84 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
class History;
namespace Data {
class Thread;
} // namespace Data
namespace Window {
class SessionController;
} // namespace Window
namespace Ui {
class RpWidget;
} // namespace Ui
namespace HistoryView {
class SubsectionTabs final {
public:
SubsectionTabs(
not_null<Window::SessionController*> controller,
not_null<Ui::RpWidget*> parent,
not_null<Data::Thread*> thread);
~SubsectionTabs();
[[nodiscard]] bool switchTo(
not_null<Data::Thread*> thread,
not_null<Ui::RpWidget*> parent);
[[nodiscard]] static bool UsedFor(not_null<Data::Thread*> thread);
[[nodiscard]] rpl::producer<> removeRequests() const;
void extractToParent(not_null<Ui::RpWidget*> parent);
void setBoundingRect(QRect boundingRect);
[[nodiscard]] rpl::producer<> layoutRequests() const;
[[nodiscard]] int leftSkip() const;
[[nodiscard]] int topSkip() const;
void raise();
void show();
void hide();
private:
void track();
void setupHorizontal(not_null<QWidget*> parent);
void setupVertical(not_null<QWidget*> parent);
void toggleModes();
void setVisible(bool shown);
void refreshSlice();
const not_null<Window::SessionController*> _controller;
const not_null<History*> _history;
Ui::RpWidget *_horizontal = nullptr;
Ui::RpWidget *_vertical = nullptr;
Ui::RpWidget *_shadow = nullptr;
std::vector<not_null<Data::Thread*>> _slice;
not_null<Data::Thread*> _active;
not_null<Data::Thread*> _around;
int _beforeLimit = 0;
int _afterLimit = 0;
std::optional<int> _beforeSkipped;
std::optional<int> _afterSkipped;
rpl::event_stream<> _layoutRequests;
rpl::event_stream<> _refreshed;
rpl::lifetime _lifetime;
};
} // namespace HistoryView

View file

@ -2193,9 +2193,10 @@ Ui::MultiSlideTracker DetailsFiller::fillChannelButtons(
}) | rpl::distinct_until_changed(); }) | rpl::distinct_until_changed();
auto viewDirect = [=] { auto viewDirect = [=] {
if (const auto linked = channel->monoforumLink()) { if (const auto linked = channel->monoforumLink()) {
if (const auto monoforum = linked->monoforum()) { window->showPeerHistory(linked);
window->showMonoforum(monoforum); //if (const auto monoforum = linked->monoforum()) {
} // window->showMonoforum(monoforum);
//}
} }
}; };
AddMainButton( // #TODO monoforum AddMainButton( // #TODO monoforum

View file

@ -1516,7 +1516,7 @@ void MainWidget::showHistory(
: Window::SlideDirection::FromRight, : Window::SlideDirection::FromRight,
animationParams); animationParams);
} else { } else {
_history->show(); _history->showFast();
crl::on_main(this, [=] { crl::on_main(this, [=] {
_controller->widget()->setInnerFocus(); _controller->widget()->setInnerFocus();
}); });
@ -1536,6 +1536,8 @@ void MainWidget::showHistory(
} }
floatPlayerCheckVisibility(); floatPlayerCheckVisibility();
controller()->dropSubsectionTabs();
} }
void MainWidget::showMessage( void MainWidget::showMessage(

View file

@ -373,7 +373,7 @@ ShortcutMessages::ShortcutMessages(
this, this,
&controller->session(), &controller->session(),
static_cast<ListDelegate*>(this)); static_cast<ListDelegate*>(this));
_inner->overrideIsChatWide(false); _inner->overrideChatMode(ElementChatMode::Default);
_scroll->sizeValue() | rpl::filter([](QSize size) { _scroll->sizeValue() | rpl::filter([](QSize size) {
return !size.isEmpty(); return !size.isEmpty();

View file

@ -1253,3 +1253,34 @@ newPeerUserpicsPadding: margins(0px, 3px, 0px, 0px);
newPeerWidth: 320px; newPeerWidth: 320px;
swipeBackSize: 150px; swipeBackSize: 150px;
chatTabsToggle: IconButton(defaultIconButton) {
width: 56px;
height: 36px;
icon: icon {{ "top_bar_profile-flip_horizontal", menuIconFg }};
iconOver: icon {{ "top_bar_profile-flip_horizontal", menuIconFgOver }};
ripple: emptyRippleAnimation;
}
chatTabsToggleActive: icon {{ "top_bar_profile-flip_horizontal", windowActiveTextFg }};
chatTabsScroll: ScrollArea(defaultScrollArea) {
barHidden: true;
}
chatTabsSlider: SettingsSlider(defaultSettingsSlider) {
padding: 0px;
height: 36px;
barTop: 33px;
barSkip: 0px;
barStroke: 6px;
barRadius: 2px;
barFg: transparent;
barSnapToLabel: true;
strictSkip: 18px;
labelTop: 9px;
labelStyle: semiboldTextStyle;
labelFg: windowSubTextFg;
labelFgActive: lightButtonFg;
rippleBottomSkip: 1px;
rippleBg: windowBgOver;
rippleBgActive: lightButtonBgOver;
ripple: defaultRippleAnimation;
}

View file

@ -279,6 +279,7 @@ void SectionWidget::setGeometryWithTopMoved(
void SectionWidget::showAnimated( void SectionWidget::showAnimated(
SlideDirection direction, SlideDirection direction,
const SectionSlideParams &params) { const SectionSlideParams &params) {
validateSubsectionTabs();
if (_showAnimation) { if (_showAnimation) {
return; return;
} }
@ -309,6 +310,7 @@ std::shared_ptr<SectionMemento> SectionWidget::createMemento() {
} }
void SectionWidget::showFast() { void SectionWidget::showFast() {
validateSubsectionTabs();
show(); show();
showFinished(); showFinished();
} }

View file

@ -194,6 +194,9 @@ public:
return nullptr; return nullptr;
} }
virtual void validateSubsectionTabs() {
}
static void PaintBackground( static void PaintBackground(
not_null<SessionController*> controller, not_null<SessionController*> controller,
not_null<Ui::ChatTheme*> theme, not_null<Ui::ChatTheme*> theme,

View file

@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
//#include "history/view/reactions/history_view_reactions_button.h" //#include "history/view/reactions/history_view_reactions_button.h"
#include "history/view/history_view_chat_section.h" #include "history/view/history_view_chat_section.h"
#include "history/view/history_view_scheduled_section.h" #include "history/view/history_view_scheduled_section.h"
#include "history/view/history_view_subsection_tabs.h"
#include "media/player/media_player_instance.h" #include "media/player/media_player_instance.h"
#include "media/view/media_view_open_common.h" #include "media/view/media_view_open_common.h"
#include "data/stickers/data_custom_emoji.h" #include "data/stickers/data_custom_emoji.h"
@ -3440,8 +3441,37 @@ std::shared_ptr<ChatHelpers::Show> SessionController::uiShow() {
return _cachedShow; return _cachedShow;
} }
void SessionController::saveSubsectionTabs(
std::unique_ptr<HistoryView::SubsectionTabs> tabs) {
_savedSubsectionTabsLifetime.destroy();
_savedSubsectionTabs = std::move(tabs);
_savedSubsectionTabs->extractToParent(widget());
_savedSubsectionTabs->removeRequests() | rpl::start_with_next([=] {
_savedSubsectionTabs = nullptr;
}, _savedSubsectionTabsLifetime);
}
auto SessionController::restoreSubsectionTabsFor(
not_null<Ui::RpWidget*> parent,
not_null<Data::Thread*> thread)
-> std::unique_ptr<HistoryView::SubsectionTabs> {
if (!_savedSubsectionTabs) {
return nullptr;
} else if (_savedSubsectionTabs->switchTo(thread, parent)) {
_savedSubsectionTabsLifetime.destroy();
return base::take(_savedSubsectionTabs);
}
return nullptr;
}
void SessionController::dropSubsectionTabs() {
_savedSubsectionTabsLifetime.destroy();
base::take(_savedSubsectionTabs);
}
SessionController::~SessionController() { SessionController::~SessionController() {
resetFakeUnreadWhileOpened(); resetFakeUnreadWhileOpened();
dropSubsectionTabs();
} }
bool CheckAndJumpToNearChatsFilter( bool CheckAndJumpToNearChatsFilter(

View file

@ -78,6 +78,10 @@ class SavedSublist;
class WallPaper; class WallPaper;
} // namespace Data } // namespace Data
namespace HistoryView {
class SubsectionTabs;
} // namespace HistoryView
namespace HistoryView::Reactions { namespace HistoryView::Reactions {
class CachedIconFactory; class CachedIconFactory;
} // namespace HistoryView::Reactions } // namespace HistoryView::Reactions
@ -659,6 +663,14 @@ public:
[[nodiscard]] std::shared_ptr<ChatHelpers::Show> uiShow() override; [[nodiscard]] std::shared_ptr<ChatHelpers::Show> uiShow() override;
void saveSubsectionTabs(
std::unique_ptr<HistoryView::SubsectionTabs> tabs);
[[nodiscard]] auto restoreSubsectionTabsFor(
not_null<Ui::RpWidget*> parent,
not_null<Data::Thread*> thread)
-> std::unique_ptr<HistoryView::SubsectionTabs>;
void dropSubsectionTabs();
[[nodiscard]] rpl::lifetime &lifetime() { [[nodiscard]] rpl::lifetime &lifetime() {
return _lifetime; return _lifetime;
} }
@ -774,6 +786,8 @@ private:
base::has_weak_ptr _storyOpenGuard; base::has_weak_ptr _storyOpenGuard;
QString _premiumRef; QString _premiumRef;
std::unique_ptr<HistoryView::SubsectionTabs> _savedSubsectionTabs;
rpl::lifetime _savedSubsectionTabsLifetime;
rpl::lifetime _lifetime; rpl::lifetime _lifetime;