mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-07-27 07:52:57 +02:00
Start new tabs for monoforums.
This commit is contained in:
parent
3dbdecf73d
commit
fdbdeeb956
28 changed files with 869 additions and 181 deletions
|
@ -888,6 +888,8 @@ PRIVATE
|
|||
history/view/history_view_sponsored_click_handler.h
|
||||
history/view/history_view_sticker_toast.cpp
|
||||
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.h
|
||||
history/view/history_view_transcribe_button.cpp
|
||||
|
|
|
@ -52,7 +52,7 @@ public:
|
|||
bool elementAnimationsPaused() override;
|
||||
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
|
||||
HistoryView::Context elementContext() override;
|
||||
bool elementIsChatWide() override;
|
||||
HistoryView::ElementChatMode elementChatMode() override;
|
||||
|
||||
private:
|
||||
const not_null<QWidget*> _parent;
|
||||
|
@ -83,8 +83,9 @@ HistoryView::Context PreviewDelegate::elementContext() {
|
|||
return HistoryView::Context::TTLViewer;
|
||||
}
|
||||
|
||||
bool PreviewDelegate::elementIsChatWide() {
|
||||
return _chatWide.current();
|
||||
HistoryView::ElementChatMode PreviewDelegate::elementChatMode() {
|
||||
using Mode = HistoryView::ElementChatMode;
|
||||
return _chatWide.current() ? Mode::Wide : Mode::Default;
|
||||
}
|
||||
|
||||
class PreviewWrap final : public Ui::RpWidget {
|
||||
|
|
|
@ -940,27 +940,27 @@ void Widget::chosenRow(const ChosenRow &row) {
|
|||
}
|
||||
}
|
||||
return;
|
||||
} else if (history
|
||||
&& history->peer->amMonoforumAdmin()
|
||||
&& !row.message.fullId) {
|
||||
const auto monoforum = history->peer->monoforum();
|
||||
if (controller()->shownMonoforum().current() == monoforum) {
|
||||
controller()->closeMonoforum();
|
||||
//} else if (row.newWindow) { // #TODO monoforum
|
||||
// controller()->showInNewWindow(
|
||||
// Window::SeparateId(Window::SeparateType::Forum, history));
|
||||
} else {
|
||||
controller()->showMonoforum(
|
||||
monoforum,
|
||||
Window::SectionShow().withChildColumn());
|
||||
if (!controller()->adaptive().isOneColumn()) {
|
||||
controller()->showThread(
|
||||
history,
|
||||
ShowAtUnreadMsgId,
|
||||
Window::SectionShow::Way::ClearStack);
|
||||
}
|
||||
}
|
||||
return;
|
||||
//} else if (history
|
||||
// && history->peer->amMonoforumAdmin()
|
||||
// && !row.message.fullId) {
|
||||
// const auto monoforum = history->peer->monoforum();
|
||||
// if (controller()->shownMonoforum().current() == monoforum) {
|
||||
// controller()->closeMonoforum();
|
||||
// //} else if (row.newWindow) { // #TODO monoforum
|
||||
// // controller()->showInNewWindow(
|
||||
// // Window::SeparateId(Window::SeparateType::Forum, history));
|
||||
// } else {
|
||||
// controller()->showMonoforum(
|
||||
// monoforum,
|
||||
// Window::SectionShow().withChildColumn());
|
||||
// if (!controller()->adaptive().isOneColumn()) {
|
||||
// controller()->showThread(
|
||||
// history,
|
||||
// ShowAtUnreadMsgId,
|
||||
// Window::SectionShow::Way::ClearStack);
|
||||
// }
|
||||
// }
|
||||
// return;
|
||||
} else if (history) {
|
||||
const auto peer = history->peer;
|
||||
const auto showAtMsgId = controller()->uniqueChatsInSearchResults()
|
||||
|
@ -999,13 +999,17 @@ void Widget::chosenRow(const ChosenRow &row) {
|
|||
using namespace Window;
|
||||
auto params = SectionShow(SectionShow::Way::Forward);
|
||||
params.dropSameFromStack = true;
|
||||
using namespace HistoryView;
|
||||
controller()->showSection(
|
||||
std::make_shared<ChatMemento>(ChatViewId{
|
||||
.history = sublist->owningHistory(),
|
||||
.sublist = sublist,
|
||||
}),
|
||||
params);
|
||||
params.highlightPart.text = _searchState.query;
|
||||
if (!params.highlightPart.empty()) {
|
||||
params.highlightPartOffsetHint = kSearchQueryOffsetHint;
|
||||
}
|
||||
if (false && row.newWindow) { // #TODO monoforum
|
||||
controller()->showInNewWindow(
|
||||
Window::SeparateId(sublist),
|
||||
row.message.fullId.msg);
|
||||
} else {
|
||||
controller()->showThread(sublist, row.message.fullId.msg, params);
|
||||
}
|
||||
}
|
||||
if (row.filteredRow && !session().supportMode()) {
|
||||
if (_subsectionTopBar) {
|
||||
|
|
|
@ -152,7 +152,7 @@ void TopicsView::prepare(PeerId frontPeerId, Fn<void()> customEmojiRepaint) {
|
|||
Ui::Text::SingleCustomEmoji(
|
||||
manager->peerUserpicEmojiData(peer),
|
||||
u"@"_q)
|
||||
).append(peer->shortName());
|
||||
).append(' ').append(peer->shortName());
|
||||
title.key = key;
|
||||
title.version = peer->nameVersion();
|
||||
title.unread = unread;
|
||||
|
|
|
@ -740,8 +740,9 @@ void InnerWidget::elementSearchInList(
|
|||
void InnerWidget::elementHandleViaClick(not_null<UserData*> bot) {
|
||||
}
|
||||
|
||||
bool InnerWidget::elementIsChatWide() {
|
||||
return _isChatWide;
|
||||
HistoryView::ElementChatMode InnerWidget::elementChatMode() {
|
||||
using Mode = HistoryView::ElementChatMode;
|
||||
return _isChatWide ? Mode::Wide : Mode::Default;
|
||||
}
|
||||
|
||||
not_null<Ui::PathShiftGradient*> InnerWidget::elementPathShiftGradient() {
|
||||
|
|
|
@ -131,7 +131,7 @@ public:
|
|||
const QString &query,
|
||||
const FullMsgId &context) override;
|
||||
void elementHandleViaClick(not_null<UserData*> bot) override;
|
||||
bool elementIsChatWide() override;
|
||||
HistoryView::ElementChatMode elementChatMode() override;
|
||||
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
|
||||
void elementReplyTo(const FullReplyTo &to) override;
|
||||
void elementStartInteraction(
|
||||
|
|
|
@ -241,8 +241,9 @@ public:
|
|||
_widget->elementHandleViaClick(bot);
|
||||
}
|
||||
}
|
||||
bool elementIsChatWide() override {
|
||||
return _widget ? _widget->elementIsChatWide() : false;
|
||||
HistoryView::ElementChatMode elementChatMode() override {
|
||||
using Mode = HistoryView::ElementChatMode;
|
||||
return _widget ? _widget->elementChatMode() : Mode::Default;
|
||||
}
|
||||
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override {
|
||||
Expects(_widget != nullptr);
|
||||
|
@ -808,7 +809,11 @@ bool HistoryInner::canHaveFromUserpics() const {
|
|||
} else if (const auto channel = _peer->asBroadcast()) {
|
||||
return channel->signatureProfiles();
|
||||
}
|
||||
return true;
|
||||
return !_removeFromUserpics;
|
||||
}
|
||||
|
||||
void HistoryInner::toggleRemoveFromUserpics(bool remove) {
|
||||
_removeFromUserpics = remove;
|
||||
}
|
||||
|
||||
template <typename Method>
|
||||
|
@ -3930,8 +3935,13 @@ void HistoryInner::elementHandleViaClick(not_null<UserData*> bot) {
|
|||
_widget->insertBotCommand('@' + bot->username());
|
||||
}
|
||||
|
||||
bool HistoryInner::elementIsChatWide() {
|
||||
return _isChatWide;
|
||||
HistoryView::ElementChatMode HistoryInner::elementChatMode() {
|
||||
using Mode = HistoryView::ElementChatMode;
|
||||
return _isChatWide
|
||||
? Mode::Wide
|
||||
: _removeFromUserpics
|
||||
? Mode::Narrow
|
||||
: Mode::Default;
|
||||
}
|
||||
|
||||
not_null<Ui::PathShiftGradient*> HistoryInner::elementPathShiftGradient() {
|
||||
|
|
|
@ -35,6 +35,7 @@ struct SelectionModeResult;
|
|||
struct StateRequest;
|
||||
enum class CursorState : char;
|
||||
enum class PointState : char;
|
||||
enum class ElementChatMode : char;
|
||||
class EmptyPainter;
|
||||
class Element;
|
||||
class TranslateTracker;
|
||||
|
@ -165,7 +166,7 @@ public:
|
|||
const QString &query,
|
||||
const FullMsgId &context);
|
||||
void elementHandleViaClick(not_null<UserData*> bot);
|
||||
bool elementIsChatWide();
|
||||
HistoryView::ElementChatMode elementChatMode();
|
||||
not_null<Ui::PathShiftGradient*> elementPathShiftGradient();
|
||||
void elementReplyTo(const FullReplyTo &to);
|
||||
void elementStartInteraction(not_null<const Element*> view);
|
||||
|
@ -193,6 +194,7 @@ public:
|
|||
|
||||
void setChooseReportReason(Data::ReportInput reportInput);
|
||||
void clearChooseReportReason();
|
||||
void toggleRemoveFromUserpics(bool remove);
|
||||
|
||||
// -1 if should not be visible, -2 if bad history()
|
||||
[[nodiscard]] int itemTop(const HistoryItem *item) const;
|
||||
|
@ -493,6 +495,7 @@ private:
|
|||
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
|
||||
QPainterPath _highlightPathCache;
|
||||
bool _isChatWide = false;
|
||||
bool _removeFromUserpics = false;
|
||||
|
||||
base::flat_set<not_null<const HistoryItem*>> _animatedStickersPlayed;
|
||||
base::flat_map<not_null<PeerData*>, Ui::PeerUserpicView> _userpics;
|
||||
|
|
|
@ -117,6 +117,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/view/history_view_reply.h"
|
||||
#include "history/view/history_view_requests_bar.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/media/history_view_media.h"
|
||||
#include "profile/profile_block_group_members.h"
|
||||
|
@ -236,6 +237,7 @@ HistoryWidget::HistoryWidget(
|
|||
, _api(&controller->session().mtp())
|
||||
, _updateEditTimeLeftDisplay([=] { updateField(); })
|
||||
, _fieldBarCancel(this, st::historyReplyCancel)
|
||||
, _topBars(std::make_unique<Ui::RpWidget>(this))
|
||||
, _topBar(this, controller)
|
||||
, _scroll(
|
||||
this,
|
||||
|
@ -1713,6 +1715,7 @@ void HistoryWidget::applyInlineBotQuery(UserData *bot, const QString &query) {
|
|||
void HistoryWidget::orderWidgets() {
|
||||
_voiceRecordBar->raise();
|
||||
_send->raise();
|
||||
_topBars->raise();
|
||||
if (_businessBotStatus) {
|
||||
_businessBotStatus->bar().raise();
|
||||
}
|
||||
|
@ -1740,6 +1743,9 @@ void HistoryWidget::orderWidgets() {
|
|||
if (_chooseTheme) {
|
||||
_chooseTheme->raise();
|
||||
}
|
||||
if (_subsectionTabs) {
|
||||
_subsectionTabs->raise();
|
||||
}
|
||||
_topShadow->raise();
|
||||
if (_autocomplete) {
|
||||
_autocomplete->raise();
|
||||
|
@ -2467,6 +2473,11 @@ void HistoryWidget::showHistory(
|
|||
_fieldDisabled = nullptr;
|
||||
_silent.destroy();
|
||||
updateBotKeyboard();
|
||||
|
||||
if (_subsectionTabs) {
|
||||
_subsectionTabsLifetime.destroy();
|
||||
controller()->saveSubsectionTabs(base::take(_subsectionTabs));
|
||||
}
|
||||
} else {
|
||||
Assert(_list == nullptr);
|
||||
}
|
||||
|
@ -2501,7 +2512,7 @@ void HistoryWidget::showHistory(
|
|||
_peer = session().data().peer(peerId);
|
||||
_contactStatus = std::make_unique<ContactStatus>(
|
||||
controller(),
|
||||
this,
|
||||
_topBars.get(),
|
||||
_peer,
|
||||
false);
|
||||
_contactStatus->bar().heightValue(
|
||||
|
@ -2514,7 +2525,7 @@ void HistoryWidget::showHistory(
|
|||
if (const auto user = _peer->asUser()) {
|
||||
_paysStatus = std::make_unique<PaysStatus>(
|
||||
controller(),
|
||||
this,
|
||||
_topBars.get(),
|
||||
user);
|
||||
_paysStatus->bar().heightValue(
|
||||
) | rpl::start_with_next([=] {
|
||||
|
@ -2522,7 +2533,7 @@ void HistoryWidget::showHistory(
|
|||
}, _paysStatus->bar().lifetime());
|
||||
_businessBotStatus = std::make_unique<BusinessBotStatus>(
|
||||
controller(),
|
||||
this,
|
||||
_topBars.get(),
|
||||
user);
|
||||
_businessBotStatus->bar().heightValue(
|
||||
) | rpl::start_with_next([=] {
|
||||
|
@ -3194,29 +3205,12 @@ void HistoryWidget::updateControlsVisibility() {
|
|||
} else if (!_firstLoadRequest && _scroll->isHidden()) {
|
||||
_scroll->show();
|
||||
}
|
||||
if (_pinnedBar) {
|
||||
_pinnedBar->show();
|
||||
}
|
||||
_topBars->show();
|
||||
if (_sponsoredMessageBar && checkSponsoredMessageBarVisibility()) {
|
||||
_sponsoredMessageBar->toggle(true, anim::type::normal);
|
||||
}
|
||||
if (_translateBar) {
|
||||
_translateBar->show();
|
||||
}
|
||||
if (_groupCallBar) {
|
||||
_groupCallBar->show();
|
||||
}
|
||||
if (_requestsBar) {
|
||||
_requestsBar->show();
|
||||
}
|
||||
if (_paysStatus) {
|
||||
_paysStatus->show();
|
||||
}
|
||||
if (_contactStatus) {
|
||||
_contactStatus->show();
|
||||
}
|
||||
if (_businessBotStatus) {
|
||||
_businessBotStatus->show();
|
||||
if (_subsectionTabs) {
|
||||
_subsectionTabs->show();
|
||||
}
|
||||
if (isChoosingTheme()
|
||||
|| (!editingMessage()
|
||||
|
@ -4431,20 +4425,12 @@ void HistoryWidget::hideChildWidgets() {
|
|||
if (_tabbedPanel) {
|
||||
_tabbedPanel->hideFast();
|
||||
}
|
||||
if (_pinnedBar) {
|
||||
_pinnedBar->hide();
|
||||
}
|
||||
if (_sponsoredMessageBar) {
|
||||
_sponsoredMessageBar->toggle(false, anim::type::instant);
|
||||
}
|
||||
if (_translateBar) {
|
||||
_translateBar->hide();
|
||||
}
|
||||
if (_groupCallBar) {
|
||||
_groupCallBar->hide();
|
||||
}
|
||||
if (_requestsBar) {
|
||||
_requestsBar->hide();
|
||||
_topBars->hide();
|
||||
if (_subsectionTabs) {
|
||||
_subsectionTabs->hide();
|
||||
}
|
||||
if (_voiceRecordBar) {
|
||||
_voiceRecordBar->hideFast();
|
||||
|
@ -4455,15 +4441,6 @@ void HistoryWidget::hideChildWidgets() {
|
|||
if (_chooseTheme) {
|
||||
_chooseTheme->hide();
|
||||
}
|
||||
if (_paysStatus) {
|
||||
_paysStatus->hide();
|
||||
}
|
||||
if (_contactStatus) {
|
||||
_contactStatus->hide();
|
||||
}
|
||||
if (_businessBotStatus) {
|
||||
_businessBotStatus->hide();
|
||||
}
|
||||
hideChildren();
|
||||
}
|
||||
|
||||
|
@ -4747,6 +4724,8 @@ MsgId HistoryWidget::msgId() const {
|
|||
void HistoryWidget::showAnimated(
|
||||
Window::SlideDirection direction,
|
||||
const Window::SectionSlideParams ¶ms) {
|
||||
validateSubsectionTabs();
|
||||
|
||||
_showAnimation = nullptr;
|
||||
|
||||
// If we show pinned bar here, we don't want it to change the
|
||||
|
@ -4791,6 +4770,11 @@ void HistoryWidget::showAnimated(
|
|||
activate();
|
||||
}
|
||||
|
||||
void HistoryWidget::showFast() {
|
||||
validateSubsectionTabs();
|
||||
show();
|
||||
}
|
||||
|
||||
void HistoryWidget::showFinished() {
|
||||
_cornerButtons.finishAnimations();
|
||||
if (_pinnedBar) {
|
||||
|
@ -6419,40 +6403,50 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) {
|
|||
}
|
||||
|
||||
void HistoryWidget::updateControlsGeometry() {
|
||||
_topBar->resizeToWidth(width());
|
||||
const auto width = this->width();
|
||||
|
||||
_topBar->resizeToWidth(width);
|
||||
_topBar->moveToLeft(0, 0);
|
||||
_voiceRecordBar->resizeToWidth(width());
|
||||
|
||||
const auto tabsLeftSkip = _subsectionTabs
|
||||
? _subsectionTabs->leftSkip()
|
||||
: 0;
|
||||
const auto innerWidth = width - tabsLeftSkip;
|
||||
|
||||
_voiceRecordBar->resizeToWidth(width);
|
||||
|
||||
moveFieldControls();
|
||||
|
||||
const auto groupCallTop = _topBar->bottomNoMargins();
|
||||
_topBars->move(tabsLeftSkip, _topBar->bottomNoMargins()
|
||||
+ (_subsectionTabs ? _subsectionTabs->topSkip() : 0));
|
||||
const auto groupCallTop = 0;
|
||||
if (_groupCallBar) {
|
||||
_groupCallBar->move(0, groupCallTop);
|
||||
_groupCallBar->resizeToWidth(width());
|
||||
_groupCallBar->resizeToWidth(innerWidth);
|
||||
}
|
||||
const auto requestsTop = groupCallTop
|
||||
+ (_groupCallBar ? _groupCallBar->height() : 0);
|
||||
if (_requestsBar) {
|
||||
_requestsBar->move(0, requestsTop);
|
||||
_requestsBar->resizeToWidth(width());
|
||||
_requestsBar->resizeToWidth(innerWidth);
|
||||
}
|
||||
const auto pinnedBarTop = requestsTop
|
||||
+ (_requestsBar ? _requestsBar->height() : 0);
|
||||
if (_pinnedBar) {
|
||||
_pinnedBar->move(0, pinnedBarTop);
|
||||
_pinnedBar->resizeToWidth(width());
|
||||
_pinnedBar->resizeToWidth(innerWidth);
|
||||
}
|
||||
const auto sponsoredMessageBarTop = pinnedBarTop
|
||||
+ (_pinnedBar ? _pinnedBar->height() : 0);
|
||||
if (_sponsoredMessageBar) {
|
||||
_sponsoredMessageBar->move(0, sponsoredMessageBarTop);
|
||||
_sponsoredMessageBar->resizeToWidth(width());
|
||||
_sponsoredMessageBar->resizeToWidth(innerWidth);
|
||||
}
|
||||
const auto translateTop = sponsoredMessageBarTop
|
||||
+ (_sponsoredMessageBar ? _sponsoredMessageBar->height() : 0);
|
||||
if (_translateBar) {
|
||||
_translateBar->move(0, translateTop);
|
||||
_translateBar->resizeToWidth(width());
|
||||
_translateBar->resizeToWidth(innerWidth);
|
||||
}
|
||||
const auto paysStatusTop = translateTop
|
||||
+ (_translateBar ? _translateBar->height() : 0);
|
||||
|
@ -6462,17 +6456,19 @@ void HistoryWidget::updateControlsGeometry() {
|
|||
const auto contactStatusTop = paysStatusTop
|
||||
+ (_paysStatus ? _paysStatus->bar().height() : 0);
|
||||
if (_contactStatus) {
|
||||
_contactStatus->bar().move(0, contactStatusTop);
|
||||
_contactStatus->bar().move(tabsLeftSkip, contactStatusTop);
|
||||
}
|
||||
const auto businessBotTop = contactStatusTop
|
||||
+ (_contactStatus ? _contactStatus->bar().height() : 0);
|
||||
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);
|
||||
_topBars->resize(innerWidth, scrollAreaTop - _topBars->y());
|
||||
if (_scroll->y() != scrollAreaTop) {
|
||||
_scroll->moveToLeft(0, scrollAreaTop);
|
||||
_scroll->moveToLeft(tabsLeftSkip, scrollAreaTop);
|
||||
if (_autocomplete) {
|
||||
_autocomplete->setBoundings(_scroll->geometry());
|
||||
}
|
||||
|
@ -6502,7 +6498,7 @@ void HistoryWidget::updateControlsGeometry() {
|
|||
_topShadow->setGeometryToLeft(
|
||||
topShadowLeft,
|
||||
_topBar->bottomNoMargins(),
|
||||
width() - topShadowLeft - topShadowRight,
|
||||
width - topShadowLeft - topShadowRight,
|
||||
st::lineWidth);
|
||||
}
|
||||
|
||||
|
@ -6704,7 +6700,12 @@ void HistoryWidget::updateHistoryGeometry(
|
|||
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) {
|
||||
newScrollHeight -= _translateBar->height();
|
||||
}
|
||||
|
@ -6760,10 +6761,10 @@ void HistoryWidget::updateHistoryGeometry(
|
|||
}
|
||||
const auto wasScrollTop = _scroll->scrollTop();
|
||||
const auto wasAtBottom = (wasScrollTop == _scroll->scrollTopMax());
|
||||
const auto needResize = (_scroll->width() != width())
|
||||
const auto needResize = (_scroll->width() != newScrollWidth)
|
||||
|| (_scroll->height() != newScrollHeight);
|
||||
if (needResize) {
|
||||
_scroll->resize(width(), newScrollHeight);
|
||||
_scroll->resize(newScrollWidth, newScrollHeight);
|
||||
// on initial updateListSize we didn't put the _scroll->scrollTop
|
||||
// correctly yet so visibleAreaUpdated() call will erase it
|
||||
// with the new (undefined) value
|
||||
|
@ -6781,6 +6782,12 @@ void HistoryWidget::updateHistoryGeometry(
|
|||
_cornerButtons.updatePositions();
|
||||
controller()->floatPlayerAreaUpdated();
|
||||
}
|
||||
if (_subsectionTabs) {
|
||||
const auto scrollBottom = _scroll->y() + newScrollHeight;
|
||||
const auto areaHeight = scrollBottom - subsectionTabsTop;
|
||||
_subsectionTabs->setBoundingRect(
|
||||
{ 0, subsectionTabsTop, width(), areaHeight });
|
||||
}
|
||||
|
||||
updateListSize();
|
||||
_updateHistoryGeometryRequired = false;
|
||||
|
@ -7625,7 +7632,7 @@ void HistoryWidget::setupTranslateBar() {
|
|||
Expects(_history != nullptr);
|
||||
|
||||
_translateBar = std::make_unique<HistoryView::TranslateBar>(
|
||||
this,
|
||||
_topBars.get(),
|
||||
controller(),
|
||||
_history);
|
||||
|
||||
|
@ -7700,7 +7707,7 @@ void HistoryWidget::checkPinnedBarState() {
|
|||
}
|
||||
|
||||
clearHidingPinnedBar();
|
||||
_pinnedBar = std::make_unique<Ui::PinnedBar>(this, [=] {
|
||||
_pinnedBar = std::make_unique<Ui::PinnedBar>(_topBars.get(), [=] {
|
||||
return controller()->isGifPausedAtLeastFor(
|
||||
Window::GifPauseReason::Any);
|
||||
}, controller()->gifPauseLevelChanged());
|
||||
|
@ -7921,7 +7928,7 @@ void HistoryWidget::setupGroupCallBar() {
|
|||
return;
|
||||
}
|
||||
_groupCallBar = std::make_unique<Ui::GroupCallBar>(
|
||||
this,
|
||||
_topBars.get(),
|
||||
HistoryView::GroupCallBarContentByPeer(
|
||||
peer,
|
||||
st::historyGroupCallUserpics.size,
|
||||
|
@ -7974,7 +7981,7 @@ void HistoryWidget::setupRequestsBar() {
|
|||
return;
|
||||
}
|
||||
_requestsBar = std::make_unique<Ui::RequestsBar>(
|
||||
this,
|
||||
_topBars.get(),
|
||||
HistoryView::RequestsBarContentByPeer(
|
||||
peer,
|
||||
st::historyRequestsUserpics.size,
|
||||
|
@ -8087,7 +8094,7 @@ void HistoryWidget::checkSponsoredMessageBar() {
|
|||
|
||||
void HistoryWidget::createSponsoredMessageBar() {
|
||||
_sponsoredMessageBar = base::make_unique_q<Ui::SlideWrap<>>(
|
||||
this,
|
||||
_topBars.get(),
|
||||
object_ptr<Ui::RpWidget>(this));
|
||||
|
||||
_sponsoredMessageBar->entity()->resizeToWidth(_scroll->width());
|
||||
|
@ -8250,6 +8257,34 @@ void HistoryWidget::showPremiumToast(not_null<DocumentData*> 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() {
|
||||
_fieldCharsCountManager.setCount(Ui::ComputeFieldCharacterCount(_field));
|
||||
checkCharsLimitation();
|
||||
|
@ -9439,5 +9474,7 @@ HistoryWidget::~HistoryWidget() {
|
|||
|
||||
session().data().itemVisibilitiesUpdated();
|
||||
}
|
||||
_subsectionTabsLifetime.destroy();
|
||||
_subsectionTabs = nullptr;
|
||||
setTabbedPanel(nullptr);
|
||||
}
|
||||
|
|
|
@ -107,6 +107,7 @@ class Element;
|
|||
class PinnedTracker;
|
||||
class TranslateBar;
|
||||
class ComposeSearch;
|
||||
class SubsectionTabs;
|
||||
struct SelectedQuote;
|
||||
} // namespace HistoryView
|
||||
|
||||
|
@ -183,6 +184,7 @@ public:
|
|||
void showAnimated(
|
||||
Window::SlideDirection direction,
|
||||
const Window::SectionSlideParams ¶ms);
|
||||
void showFast();
|
||||
void finishAnimating();
|
||||
|
||||
void doneShow();
|
||||
|
@ -684,6 +686,8 @@ private:
|
|||
|
||||
void switchToSearch(QString query);
|
||||
|
||||
void validateSubsectionTabs();
|
||||
|
||||
void checkCharsCount();
|
||||
void checkCharsLimitation();
|
||||
|
||||
|
@ -707,6 +711,8 @@ private:
|
|||
|
||||
object_ptr<Ui::IconButton> _fieldBarCancel;
|
||||
|
||||
std::unique_ptr<Ui::RpWidget> _topBars;
|
||||
|
||||
std::unique_ptr<HistoryView::TranslateBar> _translateBar;
|
||||
int _translateBarHeight = 0;
|
||||
|
||||
|
@ -821,6 +827,8 @@ private:
|
|||
const std::unique_ptr<VoiceRecordBar> _voiceRecordBar;
|
||||
const std::unique_ptr<ForwardPanel> _forwardPanel;
|
||||
std::unique_ptr<HistoryView::ComposeSearch> _composeSearch;
|
||||
std::unique_ptr<HistoryView::SubsectionTabs> _subsectionTabs;
|
||||
rpl::lifetime _subsectionTabsLifetime;
|
||||
bool _cmdStartShown = false;
|
||||
object_ptr<Ui::InputField> _field;
|
||||
base::unique_qptr<Ui::RpWidget> _fieldDisabled;
|
||||
|
|
|
@ -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_scheduled_section.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_section.h"
|
||||
#include "history/view/history_view_translate_bar.h"
|
||||
|
@ -237,6 +238,7 @@ ChatWidget::ChatWidget(
|
|||
: nullptr)
|
||||
, _topBar(this, controller)
|
||||
, _topBarShadow(this)
|
||||
, _topBars(std::make_unique<Ui::RpWidget>(this))
|
||||
, _composeControls(std::make_unique<ComposeControls>(
|
||||
this,
|
||||
ComposeControlsDescriptor{
|
||||
|
@ -256,7 +258,8 @@ ChatWidget::ChatWidget(
|
|||
}) | rpl::type_erased()
|
||||
: 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>(
|
||||
this,
|
||||
controller->chatStyle()->value(lifetime(), st::historyScroll),
|
||||
|
@ -444,6 +447,10 @@ ChatWidget::~ChatWidget() {
|
|||
if (_repliesRootId) {
|
||||
controller()->sendingAnimation().clear();
|
||||
}
|
||||
if (_subsectionTabs) {
|
||||
_subsectionTabsLifetime.destroy();
|
||||
controller()->saveSubsectionTabs(base::take(_subsectionTabs));
|
||||
}
|
||||
if (_topic) {
|
||||
if (_topic->creating()) {
|
||||
_emptyPainter = nullptr;
|
||||
|
@ -471,9 +478,10 @@ void ChatWidget::orderWidgets() {
|
|||
if (_pinnedBar) {
|
||||
_pinnedBar->raise();
|
||||
}
|
||||
if (_topBar) {
|
||||
_topBar->raise();
|
||||
if (_subsectionTabs) {
|
||||
_subsectionTabs->raise();
|
||||
}
|
||||
_topBar->raise();
|
||||
_topBarShadow->raise();
|
||||
_composeControls->raisePanels();
|
||||
}
|
||||
|
@ -499,7 +507,7 @@ void ChatWidget::setupRootView() {
|
|||
if (_topic || !_repliesRootId) {
|
||||
return;
|
||||
}
|
||||
_repliesRootView = std::make_unique<Ui::PinnedBar>(this, [=] {
|
||||
_repliesRootView = std::make_unique<Ui::PinnedBar>(_topBars.get(), [=] {
|
||||
return controller()->isGifPausedAtLeastFor(
|
||||
Window::GifPauseReason::Any);
|
||||
}, controller()->gifPauseLevelChanged());
|
||||
|
@ -577,7 +585,9 @@ void ChatWidget::setupTopicViewer() {
|
|||
void ChatWidget::subscribeToTopic() {
|
||||
Expects(_topic != nullptr);
|
||||
|
||||
_topicReopenBar = std::make_unique<TopicReopenBar>(this, _topic);
|
||||
_topicReopenBar = std::make_unique<TopicReopenBar>(
|
||||
_topBars.get(),
|
||||
_topic);
|
||||
_topicReopenBar->bar().setVisible(!animatingShow());
|
||||
_topicReopenBarHeight = _topicReopenBar->bar().height();
|
||||
_topicReopenBar->bar().heightValue(
|
||||
|
@ -1509,6 +1519,37 @@ void ChatWidget::edit(
|
|||
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() {
|
||||
if (!_repliesRootId) {
|
||||
return;
|
||||
|
@ -1937,7 +1978,7 @@ void ChatWidget::checkPinnedBarState() {
|
|||
}
|
||||
|
||||
clearHidingPinnedBar();
|
||||
_pinnedBar = std::make_unique<Ui::PinnedBar>(this, [=] {
|
||||
_pinnedBar = std::make_unique<Ui::PinnedBar>(_topBars.get(), [=] {
|
||||
return controller()->isGifPausedAtLeastFor(
|
||||
Window::GifPauseReason::Any);
|
||||
}, controller()->gifPauseLevelChanged());
|
||||
|
@ -2252,13 +2293,10 @@ QPixmap ChatWidget::grabForShowAnimation(const Window::SectionSlideParams ¶m
|
|||
if (params.withTopBarShadow) {
|
||||
_topBarShadow->show();
|
||||
}
|
||||
if (_repliesRootView) {
|
||||
_repliesRootView->hide();
|
||||
_topBars->hide();
|
||||
if (_subsectionTabs) {
|
||||
_subsectionTabs->hide();
|
||||
}
|
||||
if (_pinnedBar) {
|
||||
_pinnedBar->hide();
|
||||
}
|
||||
_translateBar->hide();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -2529,13 +2567,20 @@ void ChatWidget::updateControlsGeometry() {
|
|||
: 0;
|
||||
_topBar->resizeToWidth(contentWidth);
|
||||
_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) {
|
||||
_repliesRootView->resizeToWidth(contentWidth);
|
||||
_repliesRootView->resizeToWidth(innerWidth);
|
||||
}
|
||||
auto top = _topBar->height() + _repliesRootViewHeight;
|
||||
auto top = _repliesRootViewHeight;
|
||||
if (_pinnedBar) {
|
||||
_pinnedBar->move(0, top);
|
||||
_pinnedBar->resizeToWidth(contentWidth);
|
||||
_pinnedBar->resizeToWidth(innerWidth);
|
||||
top += _pinnedBarHeight;
|
||||
}
|
||||
if (_topicReopenBar) {
|
||||
|
@ -2543,7 +2588,7 @@ void ChatWidget::updateControlsGeometry() {
|
|||
top += _topicReopenBar->bar().height();
|
||||
}
|
||||
_translateBar->move(0, top);
|
||||
_translateBar->resizeToWidth(contentWidth);
|
||||
_translateBar->resizeToWidth(innerWidth);
|
||||
top += _translateBarHeight;
|
||||
|
||||
auto bottom = height();
|
||||
|
@ -2563,15 +2608,18 @@ void ChatWidget::updateControlsGeometry() {
|
|||
bottom -= _composeControls->heightCurrent();
|
||||
}
|
||||
|
||||
_topBars->resize(innerWidth, top);
|
||||
top += _topBars->y();
|
||||
|
||||
const auto scrollHeight = bottom - top;
|
||||
const auto scrollSize = QSize(contentWidth, scrollHeight);
|
||||
const auto scrollSize = QSize(innerWidth, scrollHeight);
|
||||
if (_scroll->size() != scrollSize) {
|
||||
_skipScrollEvent = true;
|
||||
_scroll->resize(scrollSize);
|
||||
_inner->resizeToWidth(scrollSize.width(), _scroll->height());
|
||||
_skipScrollEvent = false;
|
||||
}
|
||||
_scroll->move(0, top);
|
||||
_scroll->move(tabsLeftSkip, top);
|
||||
if (!_scroll->isHidden()) {
|
||||
if (newScrollTop) {
|
||||
_scroll->scrollToY(*newScrollTop);
|
||||
|
@ -2581,6 +2629,13 @@ void ChatWidget::updateControlsGeometry() {
|
|||
_composeControls->move(0, bottom);
|
||||
_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();
|
||||
}
|
||||
|
||||
|
@ -2700,15 +2755,9 @@ void ChatWidget::showFinishedHook() {
|
|||
_composeControls->showFinished();
|
||||
}
|
||||
_inner->showFinished();
|
||||
if (_repliesRootView) {
|
||||
_repliesRootView->show();
|
||||
}
|
||||
if (_pinnedBar) {
|
||||
_pinnedBar->show();
|
||||
}
|
||||
_translateBar->show();
|
||||
if (_topicReopenBar) {
|
||||
_topicReopenBar->bar().show();
|
||||
_topBars->show();
|
||||
if (_subsectionTabs) {
|
||||
_subsectionTabs->show();
|
||||
}
|
||||
|
||||
// We should setup the drag area only after
|
||||
|
|
|
@ -73,6 +73,7 @@ class TopicReopenBar;
|
|||
class EmptyPainter;
|
||||
class PinnedTracker;
|
||||
class TranslateBar;
|
||||
class SubsectionTabs;
|
||||
|
||||
struct ChatViewId {
|
||||
not_null<History*> history;
|
||||
|
@ -372,6 +373,7 @@ private:
|
|||
Api::SendOptions options,
|
||||
std::optional<MsgId> localMessageId);
|
||||
|
||||
void validateSubsectionTabs() override;
|
||||
void setupEmptyPainter();
|
||||
void refreshJoinGroupButton();
|
||||
[[nodiscard]] bool emptyShown() const;
|
||||
|
@ -396,6 +398,7 @@ private:
|
|||
QPointer<ListWidget> _inner;
|
||||
object_ptr<TopBarWidget> _topBar;
|
||||
object_ptr<Ui::PlainShadow> _topBarShadow;
|
||||
std::unique_ptr<Ui::RpWidget> _topBars;
|
||||
std::unique_ptr<ComposeControls> _composeControls;
|
||||
std::unique_ptr<ComposeSearch> _composeSearch;
|
||||
std::unique_ptr<Ui::FlatButton> _joinGroup;
|
||||
|
@ -404,6 +407,8 @@ private:
|
|||
std::unique_ptr<Ui::FlatButton> _openChatButton;
|
||||
std::unique_ptr<Ui::RpWidget> _aboutHiddenAuthor;
|
||||
std::unique_ptr<EmptyPainter> _emptyPainter;
|
||||
std::unique_ptr<SubsectionTabs> _subsectionTabs;
|
||||
rpl::lifetime _subsectionTabsLifetime;
|
||||
bool _canSendTexts = false;
|
||||
bool _skipScrollEvent = false;
|
||||
bool _synteticScrollEvent = false;
|
||||
|
|
|
@ -262,8 +262,8 @@ void DefaultElementDelegate::elementHandleViaClick(
|
|||
not_null<UserData*> bot) {
|
||||
}
|
||||
|
||||
bool DefaultElementDelegate::elementIsChatWide() {
|
||||
return false;
|
||||
ElementChatMode DefaultElementDelegate::elementChatMode() {
|
||||
return ElementChatMode::Default;
|
||||
}
|
||||
|
||||
void DefaultElementDelegate::elementReplyTo(const FullReplyTo &to) {
|
||||
|
@ -410,7 +410,7 @@ void UnreadBar::paint(
|
|||
const PaintContext &context,
|
||||
int y,
|
||||
int w,
|
||||
bool chatWide) const {
|
||||
ElementChatMode mode) const {
|
||||
const auto previousTranslation = p.transform().dx();
|
||||
if (previousTranslation != 0) {
|
||||
p.translate(-previousTranslation, 0);
|
||||
|
@ -434,7 +434,7 @@ void UnreadBar::paint(
|
|||
p.setPen(st->historyUnreadBarFg());
|
||||
|
||||
int maxwidth = w;
|
||||
if (chatWide) {
|
||||
if (mode == ElementChatMode::Wide) {
|
||||
maxwidth = qMin(
|
||||
maxwidth,
|
||||
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;
|
||||
if (chatWide) {
|
||||
if (mode == ElementChatMode::Wide) {
|
||||
accumulate_min(
|
||||
width,
|
||||
st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left());
|
||||
|
@ -644,7 +644,7 @@ void ServicePreMessage::paint(
|
|||
Painter &p,
|
||||
const PaintContext &context,
|
||||
QRect g,
|
||||
bool chatWide) const {
|
||||
ElementChatMode mode) const {
|
||||
const auto top = g.top() - height - st::msgMargin.top();
|
||||
p.translate(0, top);
|
||||
|
||||
|
@ -987,7 +987,8 @@ not_null<PurchasedTag*> Element::enforcePurchasedTag() {
|
|||
int Element::AdditionalSpaceForSelectionCheckbox(
|
||||
not_null<const Element*> view,
|
||||
QRect countedGeometry) {
|
||||
if (!view->hasOutLayout() || view->delegate()->elementIsChatWide()) {
|
||||
if (!view->hasOutLayout()
|
||||
|| view->delegate()->elementChatMode() == ElementChatMode::Wide) {
|
||||
return 0;
|
||||
}
|
||||
if (countedGeometry.isEmpty()) {
|
||||
|
@ -1698,7 +1699,8 @@ bool Element::hasOutLayout() const {
|
|||
}
|
||||
|
||||
bool Element::hasRightLayout() const {
|
||||
return hasOutLayout() && !_delegate->elementIsChatWide();
|
||||
return hasOutLayout()
|
||||
&& (_delegate->elementChatMode() != ElementChatMode::Wide);
|
||||
}
|
||||
|
||||
bool Element::drawBubble() const {
|
||||
|
|
|
@ -78,6 +78,12 @@ struct SelectionModeResult {
|
|||
float64 progress = 0.0;
|
||||
};
|
||||
|
||||
enum class ElementChatMode : char {
|
||||
Default,
|
||||
Wide,
|
||||
Narrow, // monoforum with left tabs
|
||||
};
|
||||
|
||||
class Element;
|
||||
class ElementDelegate {
|
||||
public:
|
||||
|
@ -114,7 +120,7 @@ public:
|
|||
const QString &query,
|
||||
const FullMsgId &context) = 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 void elementReplyTo(const FullReplyTo &to) = 0;
|
||||
virtual void elementStartInteraction(not_null<const Element*> view) = 0;
|
||||
|
@ -169,7 +175,7 @@ public:
|
|||
const QString &query,
|
||||
const FullMsgId &context) override;
|
||||
void elementHandleViaClick(not_null<UserData*> bot) override;
|
||||
bool elementIsChatWide() override;
|
||||
ElementChatMode elementChatMode() override;
|
||||
void elementReplyTo(const FullReplyTo &to) override;
|
||||
void elementStartInteraction(not_null<const Element*> view) override;
|
||||
void elementStartPremium(
|
||||
|
@ -233,7 +239,7 @@ struct UnreadBar : RuntimeComponent<UnreadBar, Element> {
|
|||
const PaintContext &context,
|
||||
int y,
|
||||
int w,
|
||||
bool chatWide) const;
|
||||
ElementChatMode mode) const;
|
||||
|
||||
QString text;
|
||||
int width = 0;
|
||||
|
@ -305,13 +311,13 @@ private:
|
|||
struct ServicePreMessage : RuntimeComponent<ServicePreMessage, Element> {
|
||||
void init(PreparedServiceText string);
|
||||
|
||||
int resizeToWidth(int newWidth, bool chatWide);
|
||||
int resizeToWidth(int newWidth, ElementChatMode mode);
|
||||
|
||||
void paint(
|
||||
Painter &p,
|
||||
const PaintContext &context,
|
||||
QRect g,
|
||||
bool chatWide) const;
|
||||
ElementChatMode mode) const;
|
||||
[[nodiscard]] ClickHandlerPtr textState(
|
||||
QPoint point,
|
||||
const StateRequest &request,
|
||||
|
|
|
@ -1898,8 +1898,10 @@ void ListWidget::elementHandleViaClick(not_null<UserData*> bot) {
|
|||
_delegate->listHandleViaClick(bot);
|
||||
}
|
||||
|
||||
bool ListWidget::elementIsChatWide() {
|
||||
return _overrideIsChatWide.value_or(_isChatWide);
|
||||
ElementChatMode ListWidget::elementChatMode() {
|
||||
return _overrideChatMode.value_or(_isChatWide
|
||||
? ElementChatMode::Wide
|
||||
: ElementChatMode::Default);
|
||||
}
|
||||
|
||||
not_null<Ui::PathShiftGradient*> ListWidget::elementPathShiftGradient() {
|
||||
|
@ -4284,8 +4286,8 @@ void ListWidget::setEmptyInfoWidget(base::unique_qptr<Ui::RpWidget> &&w) {
|
|||
}
|
||||
}
|
||||
|
||||
void ListWidget::overrideIsChatWide(bool isWide) {
|
||||
_overrideIsChatWide = isWide;
|
||||
void ListWidget::overrideChatMode(std::optional<ElementChatMode> mode) {
|
||||
_overrideChatMode = mode;
|
||||
}
|
||||
|
||||
ListWidget::~ListWidget() {
|
||||
|
|
|
@ -428,7 +428,7 @@ public:
|
|||
const QString &query,
|
||||
const FullMsgId &context) override;
|
||||
void elementHandleViaClick(not_null<UserData*> bot) override;
|
||||
bool elementIsChatWide() override;
|
||||
ElementChatMode elementChatMode() override;
|
||||
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
|
||||
void elementReplyTo(const FullReplyTo &to) override;
|
||||
void elementStartInteraction(not_null<const Element*> view) override;
|
||||
|
@ -443,7 +443,7 @@ public:
|
|||
bool elementHideTopicButton(not_null<const Element*> view) override;
|
||||
|
||||
void setEmptyInfoWidget(base::unique_qptr<Ui::RpWidget> &&w);
|
||||
void overrideIsChatWide(bool isWide);
|
||||
void overrideChatMode(std::optional<ElementChatMode> mode);
|
||||
|
||||
~ListWidget();
|
||||
|
||||
|
@ -834,7 +834,7 @@ private:
|
|||
bool _refreshingViewer = false;
|
||||
bool _showFinished = false;
|
||||
bool _resizePending = false;
|
||||
std::optional<bool> _overrideIsChatWide;
|
||||
std::optional<ElementChatMode> _overrideChatMode;
|
||||
|
||||
// _menu must be destroyed before _whoReactedMenuLifetime.
|
||||
rpl::lifetime _whoReactedMenuLifetime;
|
||||
|
|
|
@ -1142,18 +1142,13 @@ void Message::draw(Painter &p, const PaintContext &context) const {
|
|||
}
|
||||
if (context.clip.intersects(QRect(0, aboveh, width(), unreadbarh))) {
|
||||
p.translate(0, aboveh);
|
||||
bar->paint(
|
||||
p,
|
||||
context,
|
||||
0,
|
||||
width(),
|
||||
delegate()->elementIsChatWide());
|
||||
bar->paint(p, context, 0, width(), delegate()->elementChatMode());
|
||||
p.translate(0, -aboveh);
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto service = Get<ServicePreMessage>()) {
|
||||
service->paint(p, context, g, delegate()->elementIsChatWide());
|
||||
service->paint(p, context, g, delegate()->elementChatMode());
|
||||
}
|
||||
|
||||
if (isHidden()) {
|
||||
|
@ -1549,8 +1544,8 @@ void Message::draw(Painter &p, const PaintContext &context) const {
|
|||
constexpr auto kMaxHeightRatio = 3.5;
|
||||
constexpr auto kStrokeWidth = 2.;
|
||||
constexpr auto kWaveWidth = 10.;
|
||||
const auto isLeftSize = (!context.outbg)
|
||||
|| delegate()->elementIsChatWide();
|
||||
const auto isLeftSize = !context.outbg
|
||||
|| (delegate()->elementChatMode() == ElementChatMode::Wide);
|
||||
const auto ratio = std::min(context.gestureHorizontal.ratio, 1.);
|
||||
const auto reachRatio = context.gestureHorizontal.reachRatio;
|
||||
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 &st = st::msgSelectionCheck;
|
||||
const auto right = delegate()->elementIsChatWide()
|
||||
const auto right = (delegate()->elementChatMode()
|
||||
== ElementChatMode::Wide)
|
||||
? std::min(
|
||||
int(_bubbleWidthLimit
|
||||
+ st::msgPhotoSkip
|
||||
|
@ -2465,7 +2461,7 @@ bool Message::hasFromPhoto() const {
|
|||
case Context::AdminLog:
|
||||
return true;
|
||||
case Context::Monoforum:
|
||||
return delegate()->elementIsChatWide();
|
||||
return (delegate()->elementChatMode() == ElementChatMode::Wide);
|
||||
case Context::History:
|
||||
case Context::ChatPreview:
|
||||
case Context::TTLViewer:
|
||||
|
@ -2484,8 +2480,10 @@ bool Message::hasFromPhoto() const {
|
|||
|| item->isFakeAboutView()
|
||||
|| (context() == Context::Replies && item->isDiscussionPost())) {
|
||||
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()) {
|
||||
return !hasOutLayout();
|
||||
} else if (item->Has<HistoryMessageForwarded>()) {
|
||||
|
@ -4385,12 +4383,15 @@ QRect Message::countGeometry() const {
|
|||
? media->width()
|
||||
: width();
|
||||
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()
|
||||
- st::msgMargin.left()
|
||||
- (centeredView ? st::msgMargin.left() : st::msgMargin.right());
|
||||
auto contentLeft = hasRightLayout()
|
||||
? st::msgMargin.right()
|
||||
: st::msgMargin.left();
|
||||
- (centeredView ? st::msgMargin.left() : wideSkip);
|
||||
auto contentLeft = hasRightLayout() ? wideSkip : st::msgMargin.left();
|
||||
auto contentWidth = availableWidth;
|
||||
if (hasFromPhoto()) {
|
||||
contentLeft += st::msgPhotoSkip;
|
||||
|
@ -4411,7 +4412,8 @@ QRect Message::countGeometry() const {
|
|||
contentWidth = mediaWidth;
|
||||
}
|
||||
}
|
||||
if (contentWidth < availableWidth && !delegate()->elementIsChatWide()) {
|
||||
if (contentWidth < availableWidth
|
||||
&& delegate()->elementChatMode() != ElementChatMode::Wide) {
|
||||
if (outbg) {
|
||||
contentLeft += availableWidth - contentWidth;
|
||||
} else if (centeredView) {
|
||||
|
@ -4500,7 +4502,7 @@ int Message::resizeContentGetHeight(int newWidth) {
|
|||
auto newHeight = minHeight();
|
||||
|
||||
if (const auto service = Get<ServicePreMessage>()) {
|
||||
service->resizeToWidth(newWidth, delegate()->elementIsChatWide());
|
||||
service->resizeToWidth(newWidth, delegate()->elementChatMode());
|
||||
}
|
||||
|
||||
const auto botTop = item->isFakeAboutView()
|
||||
|
@ -4515,9 +4517,14 @@ int Message::resizeContentGetHeight(int newWidth) {
|
|||
// This code duplicates countGeometry() but also resizes media.
|
||||
const auto centeredView = item->isFakeAboutView()
|
||||
|| (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
|
||||
- st::msgMargin.left()
|
||||
- (centeredView ? st::msgMargin.left() : st::msgMargin.right());
|
||||
- (centeredView ? st::msgMargin.left() : wideSkip);
|
||||
if (hasFromPhoto()) {
|
||||
if (const auto size = rightActionSize()) {
|
||||
contentWidth -= size->width() + (st::msgPhotoSkip - st::historyFastShareSize);
|
||||
|
|
|
@ -423,7 +423,7 @@ bool Service::consumeHorizontalScroll(QPoint position, int delta) {
|
|||
|
||||
QRect Service::countGeometry() const {
|
||||
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()));
|
||||
}
|
||||
auto margins = st::msgServiceMargin;
|
||||
|
@ -469,7 +469,7 @@ QSize Service::performCountCurrentSize(int newWidth) {
|
|||
+ media->resizeGetHeight(newWidth)
|
||||
+ st::msgServiceMargin.bottom();
|
||||
} else if (!text().isEmpty()) {
|
||||
if (delegate()->elementIsChatWide()) {
|
||||
if (delegate()->elementChatMode() == ElementChatMode::Wide) {
|
||||
accumulate_min(contentWidth, st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left());
|
||||
}
|
||||
contentWidth -= st::msgServiceMargin.left() + st::msgServiceMargin.left(); // two small margins
|
||||
|
@ -561,7 +561,7 @@ void Service::draw(Painter &p, const PaintContext &context) const {
|
|||
context,
|
||||
0,
|
||||
width(),
|
||||
delegate()->elementIsChatWide());
|
||||
delegate()->elementChatMode());
|
||||
p.translate(0, -aboveh);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -2193,9 +2193,10 @@ Ui::MultiSlideTracker DetailsFiller::fillChannelButtons(
|
|||
}) | rpl::distinct_until_changed();
|
||||
auto viewDirect = [=] {
|
||||
if (const auto linked = channel->monoforumLink()) {
|
||||
if (const auto monoforum = linked->monoforum()) {
|
||||
window->showMonoforum(monoforum);
|
||||
}
|
||||
window->showPeerHistory(linked);
|
||||
//if (const auto monoforum = linked->monoforum()) {
|
||||
// window->showMonoforum(monoforum);
|
||||
//}
|
||||
}
|
||||
};
|
||||
AddMainButton( // #TODO monoforum
|
||||
|
|
|
@ -1516,7 +1516,7 @@ void MainWidget::showHistory(
|
|||
: Window::SlideDirection::FromRight,
|
||||
animationParams);
|
||||
} else {
|
||||
_history->show();
|
||||
_history->showFast();
|
||||
crl::on_main(this, [=] {
|
||||
_controller->widget()->setInnerFocus();
|
||||
});
|
||||
|
@ -1536,6 +1536,8 @@ void MainWidget::showHistory(
|
|||
}
|
||||
|
||||
floatPlayerCheckVisibility();
|
||||
|
||||
controller()->dropSubsectionTabs();
|
||||
}
|
||||
|
||||
void MainWidget::showMessage(
|
||||
|
|
|
@ -373,7 +373,7 @@ ShortcutMessages::ShortcutMessages(
|
|||
this,
|
||||
&controller->session(),
|
||||
static_cast<ListDelegate*>(this));
|
||||
_inner->overrideIsChatWide(false);
|
||||
_inner->overrideChatMode(ElementChatMode::Default);
|
||||
|
||||
_scroll->sizeValue() | rpl::filter([](QSize size) {
|
||||
return !size.isEmpty();
|
||||
|
|
|
@ -1253,3 +1253,34 @@ newPeerUserpicsPadding: margins(0px, 3px, 0px, 0px);
|
|||
newPeerWidth: 320px;
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -279,6 +279,7 @@ void SectionWidget::setGeometryWithTopMoved(
|
|||
void SectionWidget::showAnimated(
|
||||
SlideDirection direction,
|
||||
const SectionSlideParams ¶ms) {
|
||||
validateSubsectionTabs();
|
||||
if (_showAnimation) {
|
||||
return;
|
||||
}
|
||||
|
@ -309,6 +310,7 @@ std::shared_ptr<SectionMemento> SectionWidget::createMemento() {
|
|||
}
|
||||
|
||||
void SectionWidget::showFast() {
|
||||
validateSubsectionTabs();
|
||||
show();
|
||||
showFinished();
|
||||
}
|
||||
|
|
|
@ -194,6 +194,9 @@ public:
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
virtual void validateSubsectionTabs() {
|
||||
}
|
||||
|
||||
static void PaintBackground(
|
||||
not_null<SessionController*> controller,
|
||||
not_null<Ui::ChatTheme*> theme,
|
||||
|
|
|
@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
//#include "history/view/reactions/history_view_reactions_button.h"
|
||||
#include "history/view/history_view_chat_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/view/media_view_open_common.h"
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
|
@ -3440,8 +3441,37 @@ std::shared_ptr<ChatHelpers::Show> SessionController::uiShow() {
|
|||
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() {
|
||||
resetFakeUnreadWhileOpened();
|
||||
dropSubsectionTabs();
|
||||
}
|
||||
|
||||
bool CheckAndJumpToNearChatsFilter(
|
||||
|
|
|
@ -78,6 +78,10 @@ class SavedSublist;
|
|||
class WallPaper;
|
||||
} // namespace Data
|
||||
|
||||
namespace HistoryView {
|
||||
class SubsectionTabs;
|
||||
} // namespace HistoryView
|
||||
|
||||
namespace HistoryView::Reactions {
|
||||
class CachedIconFactory;
|
||||
} // namespace HistoryView::Reactions
|
||||
|
@ -659,6 +663,14 @@ public:
|
|||
|
||||
[[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() {
|
||||
return _lifetime;
|
||||
}
|
||||
|
@ -774,6 +786,8 @@ private:
|
|||
base::has_weak_ptr _storyOpenGuard;
|
||||
|
||||
QString _premiumRef;
|
||||
std::unique_ptr<HistoryView::SubsectionTabs> _savedSubsectionTabs;
|
||||
rpl::lifetime _savedSubsectionTabsLifetime;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue