mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Implement correct jump to message / unread / bottom.
This commit is contained in:
parent
3999bca823
commit
eec4b72d9a
13 changed files with 402 additions and 484 deletions
|
@ -540,7 +540,7 @@ bool ForumTopic::chatListUnreadMark() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ForumTopic::chatListMutedBadge() const {
|
bool ForumTopic::chatListMutedBadge() const {
|
||||||
return true;
|
return history()->mute();
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryItem *ForumTopic::chatListMessage() const {
|
HistoryItem *ForumTopic::chatListMessage() const {
|
||||||
|
|
|
@ -5233,9 +5233,6 @@ void HistoryWidget::itemRemoved(not_null<const HistoryItem*> item) {
|
||||||
if (item == _replyEditMsg && _replyToId) {
|
if (item == _replyEditMsg && _replyToId) {
|
||||||
cancelReply();
|
cancelReply();
|
||||||
}
|
}
|
||||||
while (item == _cornerButtons.replyReturn()) {
|
|
||||||
_cornerButtons.calculateNextReplyReturn();
|
|
||||||
}
|
|
||||||
if (_kbReplyTo && item == _kbReplyTo) {
|
if (_kbReplyTo && item == _kbReplyTo) {
|
||||||
toggleKeyboard();
|
toggleKeyboard();
|
||||||
_kbReplyTo = nullptr;
|
_kbReplyTo = nullptr;
|
||||||
|
@ -5806,6 +5803,10 @@ bool HistoryWidget::cornerButtonsUnreadMayBeShown() {
|
||||||
return !_firstLoadRequest && !_voiceRecordBar->isLockPresent();
|
return !_firstLoadRequest && !_voiceRecordBar->isLockPresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HistoryWidget::cornerButtonsHas(HistoryView::CornerButtonType type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void HistoryWidget::mousePressEvent(QMouseEvent *e) {
|
void HistoryWidget::mousePressEvent(QMouseEvent *e) {
|
||||||
const auto hasSecondLayer = (_editMsgId
|
const auto hasSecondLayer = (_editMsgId
|
||||||
|| _replyToId
|
|| _replyToId
|
||||||
|
|
|
@ -330,6 +330,7 @@ private:
|
||||||
bool cornerButtonsIgnoreVisibility() override;
|
bool cornerButtonsIgnoreVisibility() override;
|
||||||
std::optional<bool> cornerButtonsDownShown() override;
|
std::optional<bool> cornerButtonsDownShown() override;
|
||||||
bool cornerButtonsUnreadMayBeShown() override;
|
bool cornerButtonsUnreadMayBeShown() override;
|
||||||
|
bool cornerButtonsHas(HistoryView::CornerButtonType type) override;
|
||||||
|
|
||||||
void checkSuggestToGigagroup();
|
void checkSuggestToGigagroup();
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_messages.h"
|
#include "data/data_messages.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_forum_topic.h"
|
#include "data/data_forum_topic.h"
|
||||||
|
#include "lang/lang_keys.h"
|
||||||
|
#include "ui/toast/toast.h"
|
||||||
#include "styles/style_chat.h"
|
#include "styles/style_chat.h"
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
|
@ -31,41 +33,41 @@ CornerButtons::CornerButtons(
|
||||||
not_null<Ui::ScrollArea*> parent,
|
not_null<Ui::ScrollArea*> parent,
|
||||||
not_null<const Ui::ChatStyle*> st,
|
not_null<const Ui::ChatStyle*> st,
|
||||||
not_null<CornerButtonsDelegate*> delegate)
|
not_null<CornerButtonsDelegate*> delegate)
|
||||||
: down(
|
: _scroll(parent)
|
||||||
|
, _delegate(delegate)
|
||||||
|
, _down(
|
||||||
parent,
|
parent,
|
||||||
st->value(parent->lifetime(), st::historyToDown))
|
st->value(parent->lifetime(), st::historyToDown))
|
||||||
, mentions(
|
, _mentions(
|
||||||
parent,
|
parent,
|
||||||
st->value(parent->lifetime(), st::historyUnreadMentions))
|
st->value(parent->lifetime(), st::historyUnreadMentions))
|
||||||
, reactions(
|
, _reactions(
|
||||||
parent,
|
parent,
|
||||||
st->value(parent->lifetime(), st::historyUnreadReactions))
|
st->value(parent->lifetime(), st::historyUnreadReactions)) {
|
||||||
, _scroll(parent)
|
_down.widget->addClickHandler([=] { downClick(); });
|
||||||
, _delegate(delegate) {
|
_mentions.widget->addClickHandler([=] { mentionsClick(); });
|
||||||
down.widget->addClickHandler([=] { downClick(); });
|
_reactions.widget->addClickHandler([=] { reactionsClick(); });
|
||||||
mentions.widget->addClickHandler([=] { mentionsClick(); });
|
|
||||||
reactions.widget->addClickHandler([=] { reactionsClick(); });
|
|
||||||
|
|
||||||
const auto filterScroll = [&](CornerButton &button) {
|
const auto filterScroll = [&](CornerButton &button) {
|
||||||
button.widget->installEventFilter(this);
|
button.widget->installEventFilter(this);
|
||||||
};
|
};
|
||||||
filterScroll(down);
|
filterScroll(_down);
|
||||||
filterScroll(mentions);
|
filterScroll(_mentions);
|
||||||
filterScroll(reactions);
|
filterScroll(_reactions);
|
||||||
|
|
||||||
SendMenu::SetupUnreadMentionsMenu(mentions.widget.data(), [=] {
|
SendMenu::SetupUnreadMentionsMenu(_mentions.widget.data(), [=] {
|
||||||
return _delegate->cornerButtonsEntry();
|
return _delegate->cornerButtonsEntry();
|
||||||
});
|
});
|
||||||
SendMenu::SetupUnreadReactionsMenu(reactions.widget.data(), [=] {
|
SendMenu::SetupUnreadReactionsMenu(_reactions.widget.data(), [=] {
|
||||||
return _delegate->cornerButtonsEntry();
|
return _delegate->cornerButtonsEntry();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CornerButtons::eventFilter(QObject *o, QEvent *e) {
|
bool CornerButtons::eventFilter(QObject *o, QEvent *e) {
|
||||||
if (e->type() == QEvent::Wheel
|
if (e->type() == QEvent::Wheel
|
||||||
&& (o == down.widget
|
&& (o == _down.widget
|
||||||
|| o == mentions.widget
|
|| o == _mentions.widget
|
||||||
|| o == reactions.widget)) {
|
|| o == _reactions.widget)) {
|
||||||
return _scroll->viewportEvent(e);
|
return _scroll->viewportEvent(e);
|
||||||
}
|
}
|
||||||
return QObject::eventFilter(o, e);
|
return QObject::eventFilter(o, e);
|
||||||
|
@ -164,13 +166,23 @@ void CornerButtons::calculateNextReplyReturn() {
|
||||||
void CornerButtons::pushReplyReturn(not_null<HistoryItem*> item) {
|
void CornerButtons::pushReplyReturn(not_null<HistoryItem*> item) {
|
||||||
_replyReturns.push_back(item->fullId());
|
_replyReturns.push_back(item->fullId());
|
||||||
_replyReturn = item;
|
_replyReturn = item;
|
||||||
|
|
||||||
|
if (!_replyReturnStarted) {
|
||||||
|
_replyReturnStarted = true;
|
||||||
|
item->history()->owner().itemRemoved(
|
||||||
|
) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
|
||||||
|
while (item == _replyReturn) {
|
||||||
|
calculateNextReplyReturn();
|
||||||
|
}
|
||||||
|
}, _down.widget->lifetime());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CornerButton &CornerButtons::buttonByType(CornerButtonType type) {
|
CornerButton &CornerButtons::buttonByType(Type type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case CornerButtonType::Down: return down;
|
case Type::Down: return _down;
|
||||||
case CornerButtonType::Mentions: return mentions;
|
case Type::Mentions: return _mentions;
|
||||||
case CornerButtonType::Reactions: return reactions;
|
case Type::Reactions: return _reactions;
|
||||||
}
|
}
|
||||||
Unexpected("Type in CornerButtons::buttonByType.");
|
Unexpected("Type in CornerButtons::buttonByType.");
|
||||||
}
|
}
|
||||||
|
@ -215,47 +227,49 @@ void CornerButtons::updateUnreadThingsVisibility() {
|
||||||
}
|
}
|
||||||
const auto entry = _delegate->cornerButtonsEntry();
|
const auto entry = _delegate->cornerButtonsEntry();
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
updateVisibility(CornerButtonType::Mentions, false);
|
updateVisibility(Type::Mentions, false);
|
||||||
updateVisibility(CornerButtonType::Reactions, false);
|
updateVisibility(Type::Reactions, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto &unreadThings = entry->session().api().unreadThings();
|
auto &unreadThings = entry->session().api().unreadThings();
|
||||||
unreadThings.preloadEnough(entry);
|
unreadThings.preloadEnough(entry);
|
||||||
|
|
||||||
const auto updateWithCount = [&](CornerButtonType type, int count) {
|
const auto updateWithCount = [&](Type type, int count) {
|
||||||
updateVisibility(
|
updateVisibility(
|
||||||
type,
|
type,
|
||||||
(count > 0) && _delegate->cornerButtonsUnreadMayBeShown());
|
(count > 0) && _delegate->cornerButtonsUnreadMayBeShown());
|
||||||
};
|
};
|
||||||
if (unreadThings.trackMentions(entry)) {
|
if (_delegate->cornerButtonsHas(Type::Mentions)
|
||||||
|
&& unreadThings.trackMentions(entry)) {
|
||||||
if (const auto count = entry->unreadMentions().count(0)) {
|
if (const auto count = entry->unreadMentions().count(0)) {
|
||||||
mentions.widget->setUnreadCount(count);
|
_mentions.widget->setUnreadCount(count);
|
||||||
}
|
}
|
||||||
updateWithCount(
|
updateWithCount(
|
||||||
CornerButtonType::Mentions,
|
Type::Mentions,
|
||||||
entry->unreadMentions().loadedCount());
|
entry->unreadMentions().loadedCount());
|
||||||
} else {
|
} else {
|
||||||
updateVisibility(CornerButtonType::Mentions, false);
|
updateVisibility(Type::Mentions, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unreadThings.trackReactions(entry)) {
|
if (_delegate->cornerButtonsHas(Type::Reactions)
|
||||||
|
&& unreadThings.trackReactions(entry)) {
|
||||||
if (const auto count = entry->unreadReactions().count(0)) {
|
if (const auto count = entry->unreadReactions().count(0)) {
|
||||||
reactions.widget->setUnreadCount(count);
|
_reactions.widget->setUnreadCount(count);
|
||||||
}
|
}
|
||||||
updateWithCount(
|
updateWithCount(
|
||||||
CornerButtonType::Reactions,
|
Type::Reactions,
|
||||||
entry->unreadReactions().loadedCount());
|
entry->unreadReactions().loadedCount());
|
||||||
} else {
|
} else {
|
||||||
updateVisibility(CornerButtonType::Reactions, false);
|
updateVisibility(Type::Reactions, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CornerButtons::updateJumpDownVisibility(std::optional<int> counter) {
|
void CornerButtons::updateJumpDownVisibility(std::optional<int> counter) {
|
||||||
if (const auto shown = _delegate->cornerButtonsDownShown()) {
|
if (const auto shown = _delegate->cornerButtonsDownShown()) {
|
||||||
updateVisibility(CornerButtonType::Down, *shown);
|
updateVisibility(Type::Down, *shown);
|
||||||
}
|
}
|
||||||
if (counter) {
|
if (counter) {
|
||||||
down.widget->setUnreadCount(*counter);
|
_down.widget->setUnreadCount(*counter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,64 +287,84 @@ void CornerButtons::updatePositions() {
|
||||||
|
|
||||||
// All corner buttons is a child widgets of _scroll, not me.
|
// All corner buttons is a child widgets of _scroll, not me.
|
||||||
|
|
||||||
const auto historyDownShown = shown(down);
|
const auto historyDownShown = shown(_down);
|
||||||
const auto unreadMentionsShown = shown(mentions);
|
const auto unreadMentionsShown = shown(_mentions);
|
||||||
const auto unreadReactionsShown = shown(reactions);
|
const auto unreadReactionsShown = shown(_reactions);
|
||||||
const auto skip = st::historyUnreadThingsSkip;
|
const auto skip = st::historyUnreadThingsSkip;
|
||||||
{
|
{
|
||||||
const auto top = anim::interpolate(
|
const auto top = anim::interpolate(
|
||||||
0,
|
0,
|
||||||
down.widget->height() + st::historyToDownPosition.y(),
|
_down.widget->height() + st::historyToDownPosition.y(),
|
||||||
historyDownShown);
|
historyDownShown);
|
||||||
down.widget->moveToRight(
|
_down.widget->moveToRight(
|
||||||
st::historyToDownPosition.x(),
|
st::historyToDownPosition.x(),
|
||||||
_scroll->height() - top);
|
_scroll->height() - top);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const auto right = anim::interpolate(
|
const auto right = anim::interpolate(
|
||||||
-mentions.widget->width(),
|
-_mentions.widget->width(),
|
||||||
st::historyToDownPosition.x(),
|
st::historyToDownPosition.x(),
|
||||||
unreadMentionsShown);
|
unreadMentionsShown);
|
||||||
const auto shift = anim::interpolate(
|
const auto shift = anim::interpolate(
|
||||||
0,
|
0,
|
||||||
down.widget->height() + skip,
|
_down.widget->height() + skip,
|
||||||
historyDownShown);
|
historyDownShown);
|
||||||
const auto top = _scroll->height()
|
const auto top = _scroll->height()
|
||||||
- mentions.widget->height()
|
- _mentions.widget->height()
|
||||||
- st::historyToDownPosition.y()
|
- st::historyToDownPosition.y()
|
||||||
- shift;
|
- shift;
|
||||||
mentions.widget->moveToRight(right, top);
|
_mentions.widget->moveToRight(right, top);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const auto right = anim::interpolate(
|
const auto right = anim::interpolate(
|
||||||
-reactions.widget->width(),
|
-_reactions.widget->width(),
|
||||||
st::historyToDownPosition.x(),
|
st::historyToDownPosition.x(),
|
||||||
unreadReactionsShown);
|
unreadReactionsShown);
|
||||||
const auto shift = anim::interpolate(
|
const auto shift = anim::interpolate(
|
||||||
0,
|
0,
|
||||||
down.widget->height() + skip,
|
_down.widget->height() + skip,
|
||||||
historyDownShown
|
historyDownShown
|
||||||
) + anim::interpolate(
|
) + anim::interpolate(
|
||||||
0,
|
0,
|
||||||
mentions.widget->height() + skip,
|
_mentions.widget->height() + skip,
|
||||||
unreadMentionsShown);
|
unreadMentionsShown);
|
||||||
const auto top = _scroll->height()
|
const auto top = _scroll->height()
|
||||||
- reactions.widget->height()
|
- _reactions.widget->height()
|
||||||
- st::historyToDownPosition.y()
|
- st::historyToDownPosition.y()
|
||||||
- shift;
|
- shift;
|
||||||
reactions.widget->moveToRight(right, top);
|
_reactions.widget->moveToRight(right, top);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkVisibility(down);
|
checkVisibility(_down);
|
||||||
checkVisibility(mentions);
|
checkVisibility(_mentions);
|
||||||
checkVisibility(reactions);
|
checkVisibility(_reactions);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CornerButtons::finishAnimations() {
|
void CornerButtons::finishAnimations() {
|
||||||
down.animation.stop();
|
_down.animation.stop();
|
||||||
mentions.animation.stop();
|
_mentions.animation.stop();
|
||||||
reactions.animation.stop();
|
_reactions.animation.stop();
|
||||||
updatePositions();
|
updatePositions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Fn<void(bool found)> CornerButtons::doneJumpFrom(
|
||||||
|
FullMsgId targetId,
|
||||||
|
FullMsgId originId) {
|
||||||
|
return [=](bool found) {
|
||||||
|
skipReplyReturn(targetId);
|
||||||
|
if (originId) {
|
||||||
|
if (const auto entry = _delegate->cornerButtonsEntry()) {
|
||||||
|
if (const auto item = entry->owner().message(originId)) {
|
||||||
|
pushReplyReturn(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
Ui::Toast::Show(
|
||||||
|
_scroll.get(),
|
||||||
|
tr::lng_message_not_found(tr::now));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace HistoryView
|
} // namespace HistoryView
|
||||||
|
|
|
@ -55,6 +55,7 @@ public:
|
||||||
[[nodiscard]] virtual bool cornerButtonsIgnoreVisibility() = 0;
|
[[nodiscard]] virtual bool cornerButtonsIgnoreVisibility() = 0;
|
||||||
[[nodiscard]] virtual std::optional<bool> cornerButtonsDownShown() = 0;
|
[[nodiscard]] virtual std::optional<bool> cornerButtonsDownShown() = 0;
|
||||||
[[nodiscard]] virtual bool cornerButtonsUnreadMayBeShown() = 0;
|
[[nodiscard]] virtual bool cornerButtonsUnreadMayBeShown() = 0;
|
||||||
|
[[nodiscard]] virtual bool cornerButtonsHas(CornerButtonType type) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CornerButtons final : private QObject {
|
class CornerButtons final : private QObject {
|
||||||
|
@ -64,6 +65,8 @@ public:
|
||||||
not_null<const Ui::ChatStyle*> st,
|
not_null<const Ui::ChatStyle*> st,
|
||||||
not_null<CornerButtonsDelegate*> delegate);
|
not_null<CornerButtonsDelegate*> delegate);
|
||||||
|
|
||||||
|
using Type = CornerButtonType;
|
||||||
|
|
||||||
void downClick();
|
void downClick();
|
||||||
void mentionsClick();
|
void mentionsClick();
|
||||||
void reactionsClick();
|
void reactionsClick();
|
||||||
|
@ -75,7 +78,7 @@ public:
|
||||||
void skipReplyReturn(FullMsgId id);
|
void skipReplyReturn(FullMsgId id);
|
||||||
void calculateNextReplyReturn();
|
void calculateNextReplyReturn();
|
||||||
|
|
||||||
void updateVisibility(CornerButtonType type, bool shown);
|
void updateVisibility(Type type, bool shown);
|
||||||
void updateUnreadThingsVisibility();
|
void updateUnreadThingsVisibility();
|
||||||
void updateJumpDownVisibility(std::optional<int> counter = {});
|
void updateJumpDownVisibility(std::optional<int> counter = {});
|
||||||
void updatePositions();
|
void updatePositions();
|
||||||
|
@ -85,26 +88,31 @@ public:
|
||||||
[[nodiscard]] HistoryItem *replyReturn() const {
|
[[nodiscard]] HistoryItem *replyReturn() const {
|
||||||
return _replyReturn;
|
return _replyReturn;
|
||||||
}
|
}
|
||||||
|
[[nodiscard]] Fn<void(bool found)> doneJumpFrom(
|
||||||
CornerButton down;
|
FullMsgId targetId,
|
||||||
CornerButton mentions;
|
FullMsgId originId);
|
||||||
CornerButton reactions;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool eventFilter(QObject *o, QEvent *e) override;
|
bool eventFilter(QObject *o, QEvent *e) override;
|
||||||
|
|
||||||
void computeCurrentReplyReturn();
|
void computeCurrentReplyReturn();
|
||||||
|
|
||||||
[[nodiscard]] CornerButton &buttonByType(CornerButtonType type);
|
[[nodiscard]] CornerButton &buttonByType(Type type);
|
||||||
[[nodiscard]] History *lookupHistory() const;
|
[[nodiscard]] History *lookupHistory() const;
|
||||||
void showAt(MsgId id);
|
void showAt(MsgId id);
|
||||||
|
|
||||||
const not_null<Ui::ScrollArea*> _scroll;
|
const not_null<Ui::ScrollArea*> _scroll;
|
||||||
const not_null<CornerButtonsDelegate*> _delegate;
|
const not_null<CornerButtonsDelegate*> _delegate;
|
||||||
|
|
||||||
|
CornerButton _down;
|
||||||
|
CornerButton _mentions;
|
||||||
|
CornerButton _reactions;
|
||||||
|
|
||||||
HistoryItem *_replyReturn = nullptr;
|
HistoryItem *_replyReturn = nullptr;
|
||||||
QVector<FullMsgId> _replyReturns;
|
QVector<FullMsgId> _replyReturns;
|
||||||
|
|
||||||
|
bool _replyReturnStarted = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace HistoryView
|
} // namespace HistoryView
|
||||||
|
|
|
@ -482,9 +482,18 @@ void ListWidget::refreshRows(const Data::MessagesSlice &old) {
|
||||||
|
|
||||||
std::optional<int> ListWidget::scrollTopForPosition(
|
std::optional<int> ListWidget::scrollTopForPosition(
|
||||||
Data::MessagePosition position) const {
|
Data::MessagePosition position) const {
|
||||||
if (position == Data::MaxMessagePosition) {
|
if (position == Data::UnreadMessagePosition) {
|
||||||
|
if (_bar.element && !_bar.hidden && _bar.focus) {
|
||||||
|
const auto shift = st::lineWidth + st::historyUnreadBarMargin;
|
||||||
|
return itemTop(_bar.element) + shift;
|
||||||
|
}
|
||||||
|
position = Data::MaxMessagePosition;
|
||||||
|
}
|
||||||
|
if (_visibleTop >= _visibleBottom) {
|
||||||
|
return std::nullopt;
|
||||||
|
} else if (position == Data::MaxMessagePosition) {
|
||||||
if (loadedAtBottom()) {
|
if (loadedAtBottom()) {
|
||||||
return height();
|
return height() - (_visibleBottom - _visibleTop);
|
||||||
}
|
}
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
} else if (_items.empty()
|
} else if (_items.empty()
|
||||||
|
@ -609,6 +618,101 @@ void ListWidget::showAroundPosition(
|
||||||
refreshViewer();
|
refreshViewer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ListWidget::jumpToBottomInsteadOfUnread() const {
|
||||||
|
// If we want to jump to unread, but we're at the unread already,
|
||||||
|
// then jump to the end of the list.
|
||||||
|
//
|
||||||
|
// That means there is no read inbox messages below us.
|
||||||
|
const auto firstReadMessage = [&]() -> Element* {
|
||||||
|
for (const auto &view : ranges::views::reverse(_items)) {
|
||||||
|
const auto item = view->data();
|
||||||
|
if (item->isRegular()
|
||||||
|
&& (item->out()
|
||||||
|
|| !_delegate->listElementShownUnread(view))) {
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}();
|
||||||
|
return !firstReadMessage || (itemTop(firstReadMessage) < _visibleBottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListWidget::showAtPosition(
|
||||||
|
Data::MessagePosition position,
|
||||||
|
anim::type animated,
|
||||||
|
Fn<void(bool found)> done) {
|
||||||
|
const auto showAtUnread = (position == Data::UnreadMessagePosition);
|
||||||
|
const auto showAtStart = (position == Data::MinMessagePosition);
|
||||||
|
const auto showAtEnd = (position == Data::MaxMessagePosition);
|
||||||
|
|
||||||
|
if (showAtUnread && jumpToBottomInsteadOfUnread()) {
|
||||||
|
showAtPosition(Data::MaxMessagePosition, animated, std::move(done));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (position.fullId.peer && position.fullId.msg) {
|
||||||
|
if (const auto item = session().data().message(position.fullId)) {
|
||||||
|
position = item->position();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showAtUnread) {
|
||||||
|
showAroundPosition(position, [=] {
|
||||||
|
if (_bar.element) {
|
||||||
|
_bar.element->destroyUnreadBar();
|
||||||
|
const auto i = ranges::find(_items, not_null{ _bar.element });
|
||||||
|
Assert(i != end(_items));
|
||||||
|
refreshAttachmentsAtIndex(i - begin(_items));
|
||||||
|
_bar = {};
|
||||||
|
}
|
||||||
|
checkUnreadBarCreation();
|
||||||
|
return showAtPositionNow(position, animated, done);
|
||||||
|
});
|
||||||
|
} else if (!showAtPositionNow(position, animated, done)) {
|
||||||
|
showAroundPosition(position, [=] {
|
||||||
|
return showAtPositionNow(position, animated, done);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ListWidget::showAtPositionNow(
|
||||||
|
Data::MessagePosition position,
|
||||||
|
anim::type animated,
|
||||||
|
Fn<void(bool found)> done) {
|
||||||
|
if (const auto scrollTop = scrollTopForPosition(position)) {
|
||||||
|
computeScrollTo(*scrollTop, position, animated);
|
||||||
|
if (position != Data::MaxMessagePosition
|
||||||
|
&& position != Data::UnreadMessagePosition) {
|
||||||
|
highlightMessage(position.fullId);
|
||||||
|
}
|
||||||
|
done(!position.fullId.peer
|
||||||
|
|| !position.fullId.msg
|
||||||
|
|| viewForItem(position.fullId));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListWidget::computeScrollTo(
|
||||||
|
int to,
|
||||||
|
Data::MessagePosition position,
|
||||||
|
anim::type animated) {
|
||||||
|
const auto currentScrollHeight = (_visibleBottom - _visibleTop);
|
||||||
|
const auto currentScrollTop = _visibleTop;
|
||||||
|
const auto wanted = std::max(
|
||||||
|
std::min(to, height() - currentScrollHeight),
|
||||||
|
0);
|
||||||
|
const auto fullDelta = (wanted - currentScrollTop);
|
||||||
|
const auto limit = currentScrollHeight;
|
||||||
|
const auto scrollDelta = std::clamp(fullDelta, -limit, limit);
|
||||||
|
const auto type = (animated == anim::type::instant)
|
||||||
|
? AnimatedScroll::None
|
||||||
|
: (std::abs(fullDelta) > limit)
|
||||||
|
? AnimatedScroll::Part
|
||||||
|
: AnimatedScroll::Full;
|
||||||
|
scrollTo(wanted, position, scrollDelta, type);
|
||||||
|
}
|
||||||
|
|
||||||
void ListWidget::checkUnreadBarCreation() {
|
void ListWidget::checkUnreadBarCreation() {
|
||||||
if (!_bar.element) {
|
if (!_bar.element) {
|
||||||
if (auto data = _delegate->listMessagesBar(_items); data.bar.element) {
|
if (auto data = _delegate->listMessagesBar(_items); data.bar.element) {
|
||||||
|
@ -1672,7 +1776,7 @@ int ListWidget::resizeGetHeight(int newWidth) {
|
||||||
|
|
||||||
const auto resizeAllItems = (_itemsWidth != newWidth);
|
const auto resizeAllItems = (_itemsWidth != newWidth);
|
||||||
auto newHeight = 0;
|
auto newHeight = 0;
|
||||||
for (auto &view : _items) {
|
for (const auto &view : _items) {
|
||||||
view->setY(newHeight);
|
view->setY(newHeight);
|
||||||
if (view->pendingResize() || resizeAllItems) {
|
if (view->pendingResize() || resizeAllItems) {
|
||||||
newHeight += view->resizeGetHeight(newWidth);
|
newHeight += view->resizeGetHeight(newWidth);
|
||||||
|
|
|
@ -202,23 +202,16 @@ public:
|
||||||
Data::MessagePosition position) const;
|
Data::MessagePosition position) const;
|
||||||
Element *viewByPosition(Data::MessagePosition position) const;
|
Element *viewByPosition(Data::MessagePosition position) const;
|
||||||
std::optional<int> scrollTopForView(not_null<Element*> view) const;
|
std::optional<int> scrollTopForView(not_null<Element*> view) const;
|
||||||
enum class AnimatedScroll {
|
|
||||||
Full,
|
|
||||||
Part,
|
|
||||||
None,
|
|
||||||
};
|
|
||||||
void scrollTo(
|
|
||||||
int scrollTop,
|
|
||||||
Data::MessagePosition attachPosition,
|
|
||||||
int delta,
|
|
||||||
AnimatedScroll type);
|
|
||||||
[[nodiscard]] bool animatedScrolling() const;
|
[[nodiscard]] bool animatedScrolling() const;
|
||||||
bool isAbovePosition(Data::MessagePosition position) const;
|
bool isAbovePosition(Data::MessagePosition position) const;
|
||||||
bool isBelowPosition(Data::MessagePosition position) const;
|
bool isBelowPosition(Data::MessagePosition position) const;
|
||||||
void highlightMessage(FullMsgId itemId);
|
void highlightMessage(FullMsgId itemId);
|
||||||
void showAroundPosition(
|
|
||||||
|
void showAtPosition(
|
||||||
Data::MessagePosition position,
|
Data::MessagePosition position,
|
||||||
Fn<bool()> overrideInitialScroll);
|
anim::type animated = anim::type::normal,
|
||||||
|
Fn<void(bool found)> done = nullptr);
|
||||||
|
void refreshViewer();
|
||||||
|
|
||||||
[[nodiscard]] TextForMimeData getSelectedText() const;
|
[[nodiscard]] TextForMimeData getSelectedText() const;
|
||||||
[[nodiscard]] MessageIdsList getSelectedIds() const;
|
[[nodiscard]] MessageIdsList getSelectedIds() const;
|
||||||
|
@ -391,13 +384,21 @@ private:
|
||||||
void onTouchSelect();
|
void onTouchSelect();
|
||||||
void onTouchScrollTimer();
|
void onTouchScrollTimer();
|
||||||
|
|
||||||
void refreshViewer();
|
|
||||||
void updateAroundPositionFromNearest(int nearestIndex);
|
void updateAroundPositionFromNearest(int nearestIndex);
|
||||||
void refreshRows(const Data::MessagesSlice &old);
|
void refreshRows(const Data::MessagesSlice &old);
|
||||||
ScrollTopState countScrollState() const;
|
ScrollTopState countScrollState() const;
|
||||||
void saveScrollState();
|
void saveScrollState();
|
||||||
void restoreScrollState();
|
void restoreScrollState();
|
||||||
|
|
||||||
|
[[nodiscard]] bool jumpToBottomInsteadOfUnread() const;
|
||||||
|
void showAroundPosition(
|
||||||
|
Data::MessagePosition position,
|
||||||
|
Fn<bool()> overrideInitialScroll);
|
||||||
|
bool showAtPositionNow(
|
||||||
|
Data::MessagePosition position,
|
||||||
|
anim::type animated,
|
||||||
|
Fn<void(bool found)> done);
|
||||||
|
|
||||||
Ui::ChatPaintContext preparePaintContext(const QRect &clip) const;
|
Ui::ChatPaintContext preparePaintContext(const QRect &clip) const;
|
||||||
|
|
||||||
Element *viewForItem(FullMsgId itemId) const;
|
Element *viewForItem(FullMsgId itemId) const;
|
||||||
|
@ -453,6 +454,21 @@ private:
|
||||||
void scrollDateHideByTimer();
|
void scrollDateHideByTimer();
|
||||||
void keepScrollDateForNow();
|
void keepScrollDateForNow();
|
||||||
|
|
||||||
|
void computeScrollTo(
|
||||||
|
int to,
|
||||||
|
Data::MessagePosition position,
|
||||||
|
anim::type animated);
|
||||||
|
enum class AnimatedScroll {
|
||||||
|
Full,
|
||||||
|
Part,
|
||||||
|
None,
|
||||||
|
};
|
||||||
|
void scrollTo(
|
||||||
|
int scrollTop,
|
||||||
|
Data::MessagePosition attachPosition,
|
||||||
|
int delta,
|
||||||
|
AnimatedScroll type);
|
||||||
|
|
||||||
void trySwitchToWordSelection();
|
void trySwitchToWordSelection();
|
||||||
void switchToWordSelection();
|
void switchToWordSelection();
|
||||||
void validateTrippleClickStartTime();
|
void validateTrippleClickStartTime();
|
||||||
|
|
|
@ -102,9 +102,10 @@ PinnedWidget::PinnedWidget(
|
||||||
this,
|
this,
|
||||||
QString(),
|
QString(),
|
||||||
st::historyComposeButton))
|
st::historyComposeButton))
|
||||||
, _scrollDown(
|
, _cornerButtons(
|
||||||
_scroll.get(),
|
_scroll.get(),
|
||||||
controller->chatStyle()->value(lifetime(), st::historyToDown)) {
|
controller->chatStyle(),
|
||||||
|
static_cast<HistoryView::CornerButtonsDelegate*>(this)) {
|
||||||
controller->chatStyle()->paletteChanged(
|
controller->chatStyle()->paletteChanged(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
_scroll->updateBars();
|
_scroll->updateBars();
|
||||||
|
@ -161,26 +162,10 @@ PinnedWidget::PinnedWidget(
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
setupClearButton();
|
setupClearButton();
|
||||||
setupScrollDownButton();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PinnedWidget::~PinnedWidget() = default;
|
PinnedWidget::~PinnedWidget() = default;
|
||||||
|
|
||||||
void PinnedWidget::setupScrollDownButton() {
|
|
||||||
_scrollDown->setClickedCallback([=] {
|
|
||||||
scrollDownClicked();
|
|
||||||
});
|
|
||||||
base::install_event_filter(_scrollDown, [=](not_null<QEvent*> event) {
|
|
||||||
if (event->type() != QEvent::Wheel) {
|
|
||||||
return base::EventFilterResult::Continue;
|
|
||||||
}
|
|
||||||
return _scroll->viewportEvent(event)
|
|
||||||
? base::EventFilterResult::Cancel
|
|
||||||
: base::EventFilterResult::Continue;
|
|
||||||
});
|
|
||||||
updateScrollDownVisibility();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PinnedWidget::setupClearButton() {
|
void PinnedWidget::setupClearButton() {
|
||||||
Data::CanPinMessagesValue(
|
Data::CanPinMessagesValue(
|
||||||
_history->peer
|
_history->peer
|
||||||
|
@ -203,118 +188,48 @@ void PinnedWidget::setupClearButton() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void PinnedWidget::scrollDownClicked() {
|
void PinnedWidget::cornerButtonsShowAtPosition(
|
||||||
if (base::IsCtrlPressed()) {
|
Data::MessagePosition position) {
|
||||||
showAtEnd();
|
showAtPosition(position);
|
||||||
//} else if (_replyReturn) {
|
}
|
||||||
// showAtPosition(_replyReturn->position());
|
|
||||||
} else {
|
Dialogs::Entry *PinnedWidget::cornerButtonsEntry() {
|
||||||
showAtEnd();
|
return _history;
|
||||||
|
}
|
||||||
|
|
||||||
|
FullMsgId PinnedWidget::cornerButtonsCurrentId() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PinnedWidget::cornerButtonsIgnoreVisibility() {
|
||||||
|
return animatingShow();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<bool> PinnedWidget::cornerButtonsDownShown() {
|
||||||
|
const auto top = _scroll->scrollTop() + st::historyToDownShownAfter;
|
||||||
|
if (top < _scroll->scrollTopMax() || _cornerButtons.replyReturn()) {
|
||||||
|
return true;
|
||||||
|
} else if (_inner->loadedAtBottomKnown()) {
|
||||||
|
return !_inner->loadedAtBottom();
|
||||||
}
|
}
|
||||||
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PinnedWidget::showAtStart() {
|
bool PinnedWidget::cornerButtonsUnreadMayBeShown() {
|
||||||
showAtPosition(Data::MinMessagePosition);
|
return _inner->loadedAtBottomKnown();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PinnedWidget::showAtEnd() {
|
bool PinnedWidget::cornerButtonsHas(CornerButtonType type) {
|
||||||
showAtPosition(Data::MaxMessagePosition);
|
return (type == CornerButtonType::Down);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PinnedWidget::showAtPosition(
|
void PinnedWidget::showAtPosition(
|
||||||
Data::MessagePosition position,
|
Data::MessagePosition position,
|
||||||
HistoryItem *originItem) {
|
FullMsgId originId) {
|
||||||
if (!showAtPositionNow(position, originItem)) {
|
_inner->showAtPosition(
|
||||||
_inner->showAroundPosition(position, [=] {
|
position,
|
||||||
return showAtPositionNow(position, originItem);
|
anim::type::normal,
|
||||||
});
|
_cornerButtons.doneJumpFrom(position.fullId, originId));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PinnedWidget::showAtPositionNow(
|
|
||||||
Data::MessagePosition position,
|
|
||||||
HistoryItem *originItem,
|
|
||||||
anim::type animated) {
|
|
||||||
using AnimatedScroll = HistoryView::ListWidget::AnimatedScroll;
|
|
||||||
|
|
||||||
const auto item = position.fullId
|
|
||||||
? _history->owner().message(position.fullId)
|
|
||||||
: nullptr;
|
|
||||||
const auto use = item ? item->position() : position;
|
|
||||||
if (const auto scrollTop = _inner->scrollTopForPosition(use)) {
|
|
||||||
const auto currentScrollTop = _scroll->scrollTop();
|
|
||||||
const auto wanted = std::clamp(
|
|
||||||
*scrollTop,
|
|
||||||
0,
|
|
||||||
_scroll->scrollTopMax());
|
|
||||||
const auto fullDelta = (wanted - currentScrollTop);
|
|
||||||
const auto limit = _scroll->height();
|
|
||||||
const auto scrollDelta = std::clamp(fullDelta, -limit, limit);
|
|
||||||
const auto type = (animated == anim::type::instant)
|
|
||||||
? AnimatedScroll::None
|
|
||||||
: (std::abs(fullDelta) > limit)
|
|
||||||
? AnimatedScroll::Part
|
|
||||||
: AnimatedScroll::Full;
|
|
||||||
_inner->scrollTo(
|
|
||||||
wanted,
|
|
||||||
use,
|
|
||||||
scrollDelta,
|
|
||||||
type);
|
|
||||||
if (use != Data::MaxMessagePosition
|
|
||||||
&& use != Data::UnreadMessagePosition) {
|
|
||||||
_inner->highlightMessage(use.fullId);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PinnedWidget::updateScrollDownVisibility() {
|
|
||||||
if (animatingShow()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto scrollDownIsVisible = [&]() -> std::optional<bool> {
|
|
||||||
const auto top = _scroll->scrollTop() + st::historyToDownShownAfter;
|
|
||||||
if (top < _scroll->scrollTopMax()) {
|
|
||||||
return true;
|
|
||||||
} else if (_inner->loadedAtBottomKnown()) {
|
|
||||||
return !_inner->loadedAtBottom();
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
};
|
|
||||||
const auto scrollDownIsShown = scrollDownIsVisible();
|
|
||||||
if (!scrollDownIsShown) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (_scrollDownIsShown != *scrollDownIsShown) {
|
|
||||||
_scrollDownIsShown = *scrollDownIsShown;
|
|
||||||
_scrollDownShown.start(
|
|
||||||
[=] { updateScrollDownPosition(); },
|
|
||||||
_scrollDownIsShown ? 0. : 1.,
|
|
||||||
_scrollDownIsShown ? 1. : 0.,
|
|
||||||
st::historyToDownDuration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PinnedWidget::updateScrollDownPosition() {
|
|
||||||
// _scrollDown is a child widget of _scroll, not me.
|
|
||||||
auto top = anim::interpolate(
|
|
||||||
0,
|
|
||||||
_scrollDown->height() + st::historyToDownPosition.y(),
|
|
||||||
_scrollDownShown.value(_scrollDownIsShown ? 1. : 0.));
|
|
||||||
_scrollDown->moveToRight(
|
|
||||||
st::historyToDownPosition.x(),
|
|
||||||
_scroll->height() - top);
|
|
||||||
auto shouldBeHidden = !_scrollDownIsShown && !_scrollDownShown.animating();
|
|
||||||
if (shouldBeHidden != _scrollDown->isHidden()) {
|
|
||||||
_scrollDown->setVisible(!shouldBeHidden);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PinnedWidget::scrollDownAnimationFinish() {
|
|
||||||
_scrollDownShown.stop();
|
|
||||||
updateScrollDownPosition();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PinnedWidget::updateAdaptiveLayout() {
|
void PinnedWidget::updateAdaptiveLayout() {
|
||||||
|
@ -387,15 +302,12 @@ void PinnedWidget::saveState(not_null<PinnedMemento*> memento) {
|
||||||
void PinnedWidget::restoreState(not_null<PinnedMemento*> memento) {
|
void PinnedWidget::restoreState(not_null<PinnedMemento*> memento) {
|
||||||
_inner->restoreState(memento->list());
|
_inner->restoreState(memento->list());
|
||||||
if (const auto highlight = memento->getHighlightId()) {
|
if (const auto highlight = memento->getHighlightId()) {
|
||||||
const auto position = Data::MessagePosition{
|
_inner->showAtPosition(Data::MessagePosition{
|
||||||
.fullId = ((highlight > 0 || !_migratedPeer)
|
.fullId = ((highlight > 0 || !_migratedPeer)
|
||||||
? FullMsgId(_history->peer->id, highlight)
|
? FullMsgId(_history->peer->id, highlight)
|
||||||
: FullMsgId(_migratedPeer->id, -highlight)),
|
: FullMsgId(_migratedPeer->id, -highlight)),
|
||||||
.date = TimeId(0),
|
.date = TimeId(0),
|
||||||
};
|
}, anim::type::instant);
|
||||||
_inner->showAroundPosition(position, [=] {
|
|
||||||
return showAtPositionNow(position, nullptr, anim::type::instant);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -463,7 +375,8 @@ void PinnedWidget::updateControlsGeometry() {
|
||||||
}
|
}
|
||||||
updateInnerVisibleArea();
|
updateInnerVisibleArea();
|
||||||
}
|
}
|
||||||
updateScrollDownPosition();
|
|
||||||
|
_cornerButtons.updatePositions();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PinnedWidget::paintEvent(QPaintEvent *e) {
|
void PinnedWidget::paintEvent(QPaintEvent *e) {
|
||||||
|
@ -490,7 +403,8 @@ void PinnedWidget::onScroll() {
|
||||||
void PinnedWidget::updateInnerVisibleArea() {
|
void PinnedWidget::updateInnerVisibleArea() {
|
||||||
const auto scrollTop = _scroll->scrollTop();
|
const auto scrollTop = _scroll->scrollTop();
|
||||||
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
|
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
|
||||||
updateScrollDownVisibility();
|
_cornerButtons.updateJumpDownVisibility();
|
||||||
|
_cornerButtons.updateUnreadThingsVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PinnedWidget::showAnimatedHook(
|
void PinnedWidget::showAnimatedHook(
|
||||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "window/section_widget.h"
|
#include "window/section_widget.h"
|
||||||
#include "window/section_memento.h"
|
#include "window/section_memento.h"
|
||||||
#include "history/view/history_view_list_widget.h"
|
#include "history/view/history_view_list_widget.h"
|
||||||
|
#include "history/view/history_view_corner_buttons.h"
|
||||||
#include "data/data_messages.h"
|
#include "data/data_messages.h"
|
||||||
#include "base/weak_ptr.h"
|
#include "base/weak_ptr.h"
|
||||||
#include "base/timer.h"
|
#include "base/timer.h"
|
||||||
|
@ -35,7 +36,8 @@ class PinnedMemento;
|
||||||
|
|
||||||
class PinnedWidget final
|
class PinnedWidget final
|
||||||
: public Window::SectionWidget
|
: public Window::SectionWidget
|
||||||
, private ListDelegate {
|
, private ListDelegate
|
||||||
|
, private CornerButtonsDelegate {
|
||||||
public:
|
public:
|
||||||
PinnedWidget(
|
PinnedWidget(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
|
@ -111,6 +113,16 @@ public:
|
||||||
-> rpl::producer<Data::AllowedReactions> override;
|
-> rpl::producer<Data::AllowedReactions> override;
|
||||||
void listShowPremiumToast(not_null<DocumentData*> document) override;
|
void listShowPremiumToast(not_null<DocumentData*> document) override;
|
||||||
|
|
||||||
|
// CornerButtonsDelegate delegate.
|
||||||
|
void cornerButtonsShowAtPosition(
|
||||||
|
Data::MessagePosition position) override;
|
||||||
|
Dialogs::Entry *cornerButtonsEntry() override;
|
||||||
|
FullMsgId cornerButtonsCurrentId() override;
|
||||||
|
bool cornerButtonsIgnoreVisibility() override;
|
||||||
|
std::optional<bool> cornerButtonsDownShown() override;
|
||||||
|
bool cornerButtonsUnreadMayBeShown() override;
|
||||||
|
bool cornerButtonsHas(CornerButtonType type) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void resizeEvent(QResizeEvent *e) override;
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
void paintEvent(QPaintEvent *e) override;
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
@ -127,22 +139,11 @@ private:
|
||||||
void updateAdaptiveLayout();
|
void updateAdaptiveLayout();
|
||||||
void saveState(not_null<PinnedMemento*> memento);
|
void saveState(not_null<PinnedMemento*> memento);
|
||||||
void restoreState(not_null<PinnedMemento*> memento);
|
void restoreState(not_null<PinnedMemento*> memento);
|
||||||
void showAtStart();
|
|
||||||
void showAtEnd();
|
|
||||||
void showAtPosition(
|
void showAtPosition(
|
||||||
Data::MessagePosition position,
|
Data::MessagePosition position,
|
||||||
HistoryItem *originItem = nullptr);
|
FullMsgId originId = {});
|
||||||
bool showAtPositionNow(
|
|
||||||
Data::MessagePosition position,
|
|
||||||
HistoryItem *originItem,
|
|
||||||
anim::type animated = anim::type::normal);
|
|
||||||
|
|
||||||
void setupClearButton();
|
void setupClearButton();
|
||||||
void setupScrollDownButton();
|
|
||||||
void scrollDownClicked();
|
|
||||||
void scrollDownAnimationFinish();
|
|
||||||
void updateScrollDownVisibility();
|
|
||||||
void updateScrollDownPosition();
|
|
||||||
|
|
||||||
void confirmDeleteSelected();
|
void confirmDeleteSelected();
|
||||||
void confirmForwardSelected();
|
void confirmForwardSelected();
|
||||||
|
@ -163,9 +164,7 @@ private:
|
||||||
std::unique_ptr<Ui::ScrollArea> _scroll;
|
std::unique_ptr<Ui::ScrollArea> _scroll;
|
||||||
std::unique_ptr<Ui::FlatButton> _clearButton;
|
std::unique_ptr<Ui::FlatButton> _clearButton;
|
||||||
|
|
||||||
Ui::Animations::Simple _scrollDownShown;
|
CornerButtons _cornerButtons;
|
||||||
bool _scrollDownIsShown = false;
|
|
||||||
object_ptr<Ui::HistoryDownButton> _scrollDown;
|
|
||||||
|
|
||||||
int _messagesCount = -1;
|
int _messagesCount = -1;
|
||||||
|
|
||||||
|
|
|
@ -337,9 +337,6 @@ RepliesWidget::RepliesWidget(
|
||||||
controller->showBackFromStack();
|
controller->showBackFromStack();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (update.item == _cornerButtons.replyReturn()) {
|
|
||||||
_cornerButtons.calculateNextReplyReturn();
|
|
||||||
}
|
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
_history->session().changes().historyUpdates(
|
_history->session().changes().historyUpdates(
|
||||||
|
@ -477,6 +474,8 @@ void RepliesWidget::setupTopicViewer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RepliesWidget::subscribeToTopic() {
|
void RepliesWidget::subscribeToTopic() {
|
||||||
|
Expects(_topic != nullptr);
|
||||||
|
|
||||||
using TopicUpdateFlag = Data::TopicUpdate::Flag;
|
using TopicUpdateFlag = Data::TopicUpdate::Flag;
|
||||||
session().changes().topicUpdates(
|
session().changes().topicUpdates(
|
||||||
_topic,
|
_topic,
|
||||||
|
@ -485,6 +484,8 @@ void RepliesWidget::subscribeToTopic() {
|
||||||
) | rpl::start_with_next([=](const Data::TopicUpdate &update) {
|
) | rpl::start_with_next([=](const Data::TopicUpdate &update) {
|
||||||
_cornerButtons.updateUnreadThingsVisibility();
|
_cornerButtons.updateUnreadThingsVisibility();
|
||||||
}, _topicLifetime);
|
}, _topicLifetime);
|
||||||
|
|
||||||
|
_cornerButtons.updateUnreadThingsVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RepliesWidget::setTopic(Data::ForumTopic *topic) {
|
void RepliesWidget::setTopic(Data::ForumTopic *topic) {
|
||||||
|
@ -917,7 +918,7 @@ bool RepliesWidget::showSlowmodeError() {
|
||||||
Ui::FormatDurationWordsSlowmode(left));
|
Ui::FormatDurationWordsSlowmode(left));
|
||||||
} else if (_history->peer->slowmodeApplied()) {
|
} else if (_history->peer->slowmodeApplied()) {
|
||||||
if (const auto item = _history->latestSendingMessage()) {
|
if (const auto item = _history->latestSendingMessage()) {
|
||||||
showAtPositionNow(item->position(), nullptr);
|
showAtPosition(item->position());
|
||||||
return tr::lng_slowmode_no_many(tr::now);
|
return tr::lng_slowmode_no_many(tr::now);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1379,7 +1380,7 @@ void RepliesWidget::cornerButtonsShowAtPosition(
|
||||||
}
|
}
|
||||||
|
|
||||||
Dialogs::Entry *RepliesWidget::cornerButtonsEntry() {
|
Dialogs::Entry *RepliesWidget::cornerButtonsEntry() {
|
||||||
return _topic;
|
return _topic ? static_cast<Dialogs::Entry*>(_topic) : _history;
|
||||||
}
|
}
|
||||||
|
|
||||||
FullMsgId RepliesWidget::cornerButtonsCurrentId() {
|
FullMsgId RepliesWidget::cornerButtonsCurrentId() {
|
||||||
|
@ -1408,6 +1409,10 @@ bool RepliesWidget::cornerButtonsUnreadMayBeShown() {
|
||||||
&& !_composeControls->isLockPresent();
|
&& !_composeControls->isLockPresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RepliesWidget::cornerButtonsHas(CornerButtonType type) {
|
||||||
|
return _topic || (type == CornerButtonType::Down);
|
||||||
|
}
|
||||||
|
|
||||||
void RepliesWidget::showAtStart() {
|
void RepliesWidget::showAtStart() {
|
||||||
showAtPosition(Data::MinMessagePosition);
|
showAtPosition(Data::MinMessagePosition);
|
||||||
}
|
}
|
||||||
|
@ -1426,50 +1431,12 @@ void RepliesWidget::finishSending() {
|
||||||
|
|
||||||
void RepliesWidget::showAtPosition(
|
void RepliesWidget::showAtPosition(
|
||||||
Data::MessagePosition position,
|
Data::MessagePosition position,
|
||||||
HistoryItem *originItem) {
|
FullMsgId originItemId) {
|
||||||
if (!showAtPositionNow(position, originItem)) {
|
_lastShownAt = position.fullId;
|
||||||
_inner->showAroundPosition(position, [=] {
|
_inner->showAtPosition(
|
||||||
return showAtPositionNow(position, originItem);
|
position,
|
||||||
});
|
anim::type::normal,
|
||||||
}
|
_cornerButtons.doneJumpFrom(position.fullId, originItemId));
|
||||||
}
|
|
||||||
|
|
||||||
bool RepliesWidget::showAtPositionNow(
|
|
||||||
Data::MessagePosition position,
|
|
||||||
HistoryItem *originItem,
|
|
||||||
anim::type animated) {
|
|
||||||
using AnimatedScroll = HistoryView::ListWidget::AnimatedScroll;
|
|
||||||
const auto item = position.fullId
|
|
||||||
? _history->owner().message(position.fullId)
|
|
||||||
: nullptr;
|
|
||||||
const auto use = item ? item->position() : position;
|
|
||||||
if (const auto scrollTop = _inner->scrollTopForPosition(use)) {
|
|
||||||
_cornerButtons.skipReplyReturn(use.fullId);
|
|
||||||
const auto currentScrollTop = _scroll->scrollTop();
|
|
||||||
const auto wanted = std::clamp(
|
|
||||||
*scrollTop,
|
|
||||||
0,
|
|
||||||
_scroll->scrollTopMax());
|
|
||||||
const auto fullDelta = (wanted - currentScrollTop);
|
|
||||||
const auto limit = _scroll->height();
|
|
||||||
const auto scrollDelta = std::clamp(fullDelta, -limit, limit);
|
|
||||||
const auto type = (animated == anim::type::instant)
|
|
||||||
? AnimatedScroll::None
|
|
||||||
: (std::abs(fullDelta) > limit)
|
|
||||||
? AnimatedScroll::Part
|
|
||||||
: AnimatedScroll::Full;
|
|
||||||
_inner->scrollTo(wanted, use, scrollDelta, type);
|
|
||||||
_lastShownAt = use.fullId;
|
|
||||||
if (use != Data::MaxMessagePosition
|
|
||||||
&& use != Data::UnreadMessagePosition) {
|
|
||||||
_inner->highlightMessage(use.fullId);
|
|
||||||
}
|
|
||||||
if (originItem) {
|
|
||||||
pushReplyReturn(originItem);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RepliesWidget::updateAdaptiveLayout() {
|
void RepliesWidget::updateAdaptiveLayout() {
|
||||||
|
@ -1619,11 +1586,10 @@ bool RepliesWidget::showMessage(
|
||||||
if (!originMessage) {
|
if (!originMessage) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const auto originItem = (!originMessage
|
const auto originItemId = (_cornerButtons.replyReturn() != originMessage)
|
||||||
|| _cornerButtons.replyReturn() == originMessage)
|
? originMessage->fullId()
|
||||||
? nullptr
|
: FullMsgId();
|
||||||
: originMessage;
|
showAtPosition(message->position(), originItemId);
|
||||||
showAtPosition(message->position(), originItem);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1653,7 +1619,7 @@ void RepliesWidget::refreshReplies() {
|
||||||
? _topic->replies()
|
? _topic->replies()
|
||||||
: std::make_shared<Data::RepliesList>(_history, _rootId));
|
: std::make_shared<Data::RepliesList>(_history, _rootId));
|
||||||
if (old) {
|
if (old) {
|
||||||
_inner->showAroundPosition(Data::UnreadMessagePosition, nullptr);
|
_inner->refreshViewer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1701,13 +1667,10 @@ void RepliesWidget::restoreState(not_null<RepliesMemento*> memento) {
|
||||||
_cornerButtons.setReplyReturns(memento->replyReturns());
|
_cornerButtons.setReplyReturns(memento->replyReturns());
|
||||||
_inner->restoreState(memento->list());
|
_inner->restoreState(memento->list());
|
||||||
if (const auto highlight = memento->getHighlightId()) {
|
if (const auto highlight = memento->getHighlightId()) {
|
||||||
const auto position = Data::MessagePosition{
|
_inner->showAtPosition(Data::MessagePosition{
|
||||||
.fullId = FullMsgId(_history->peer->id, highlight),
|
.fullId = FullMsgId(_history->peer->id, highlight),
|
||||||
.date = TimeId(0),
|
.date = TimeId(0),
|
||||||
};
|
}, anim::type::instant);
|
||||||
_inner->showAroundPosition(position, [=] {
|
|
||||||
return showAtPositionNow(position, nullptr);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -159,6 +159,7 @@ public:
|
||||||
bool cornerButtonsIgnoreVisibility() override;
|
bool cornerButtonsIgnoreVisibility() override;
|
||||||
std::optional<bool> cornerButtonsDownShown() override;
|
std::optional<bool> cornerButtonsDownShown() override;
|
||||||
bool cornerButtonsUnreadMayBeShown() override;
|
bool cornerButtonsUnreadMayBeShown() override;
|
||||||
|
bool cornerButtonsHas(CornerButtonType type) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void resizeEvent(QResizeEvent *e) override;
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
|
@ -182,11 +183,7 @@ private:
|
||||||
void showAtEnd();
|
void showAtEnd();
|
||||||
void showAtPosition(
|
void showAtPosition(
|
||||||
Data::MessagePosition position,
|
Data::MessagePosition position,
|
||||||
HistoryItem *originItem = nullptr);
|
FullMsgId originItemId = {});
|
||||||
bool showAtPositionNow(
|
|
||||||
Data::MessagePosition position,
|
|
||||||
HistoryItem *originItem,
|
|
||||||
anim::type animated = anim::type::normal);
|
|
||||||
void finishSending();
|
void finishSending();
|
||||||
|
|
||||||
void setupComposeControls();
|
void setupComposeControls();
|
||||||
|
|
|
@ -112,9 +112,10 @@ ScheduledWidget::ScheduledWidget(
|
||||||
[=](not_null<DocumentData*> emoji) { listShowPremiumToast(emoji); },
|
[=](not_null<DocumentData*> emoji) { listShowPremiumToast(emoji); },
|
||||||
ComposeControls::Mode::Scheduled,
|
ComposeControls::Mode::Scheduled,
|
||||||
SendMenu::Type::Disabled))
|
SendMenu::Type::Disabled))
|
||||||
, _scrollDown(
|
, _cornerButtons(
|
||||||
_scroll,
|
_scroll.data(),
|
||||||
controller->chatStyle()->value(lifetime(), st::historyToDown)) {
|
controller->chatStyle(),
|
||||||
|
static_cast<HistoryView::CornerButtonsDelegate*>(this)) {
|
||||||
controller->chatStyle()->paletteChanged(
|
controller->chatStyle()->paletteChanged(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
_scroll->updateBars();
|
_scroll->updateBars();
|
||||||
|
@ -193,16 +194,6 @@ ScheduledWidget::ScheduledWidget(
|
||||||
emptyInfo->setText(emptyText);
|
emptyInfo->setText(emptyText);
|
||||||
_inner->setEmptyInfoWidget(std::move(emptyInfo));
|
_inner->setEmptyInfoWidget(std::move(emptyInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
_history->session().changes().messageUpdates(
|
|
||||||
Data::MessageUpdate::Flag::Destroyed
|
|
||||||
) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
|
|
||||||
while (update.item == _replyReturn) {
|
|
||||||
calculateNextReplyReturn();
|
|
||||||
}
|
|
||||||
}, lifetime());
|
|
||||||
|
|
||||||
setupScrollDownButton();
|
|
||||||
setupComposeControls();
|
setupComposeControls();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,7 +298,8 @@ void ScheduledWidget::setupComposeControls() {
|
||||||
|
|
||||||
_composeControls->lockShowStarts(
|
_composeControls->lockShowStarts(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
updateScrollDownVisibility();
|
_cornerButtons.updateJumpDownVisibility();
|
||||||
|
_cornerButtons.updateUnreadThingsVisibility();
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
_composeControls->viewportEvents(
|
_composeControls->viewportEvents(
|
||||||
|
@ -488,40 +480,21 @@ bool ScheduledWidget::confirmSendingFiles(
|
||||||
|
|
||||||
void ScheduledWidget::pushReplyReturn(not_null<HistoryItem*> item) {
|
void ScheduledWidget::pushReplyReturn(not_null<HistoryItem*> item) {
|
||||||
if (_inner->viewByPosition(item->position())) {
|
if (_inner->viewByPosition(item->position())) {
|
||||||
_replyReturns.push_back(item->id);
|
_cornerButtons.pushReplyReturn(item);
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_replyReturn = item;
|
|
||||||
updateScrollDownVisibility();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScheduledWidget::computeCurrentReplyReturn() {
|
|
||||||
_replyReturn = _replyReturns.empty()
|
|
||||||
? nullptr
|
|
||||||
: _history->owner().message(_history->peer, _replyReturns.back());
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScheduledWidget::calculateNextReplyReturn() {
|
|
||||||
_replyReturn = nullptr;
|
|
||||||
while (!_replyReturns.empty() && !_replyReturn) {
|
|
||||||
_replyReturns.pop_back();
|
|
||||||
computeCurrentReplyReturn();
|
|
||||||
}
|
|
||||||
if (!_replyReturn) {
|
|
||||||
updateScrollDownVisibility();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScheduledWidget::checkReplyReturns() {
|
void ScheduledWidget::checkReplyReturns() {
|
||||||
const auto currentTop = _scroll->scrollTop();
|
const auto currentTop = _scroll->scrollTop();
|
||||||
for (; _replyReturn != nullptr; calculateNextReplyReturn()) {
|
while (const auto replyReturn = _cornerButtons.replyReturn()) {
|
||||||
const auto position = _replyReturn->position();
|
const auto position = replyReturn->position();
|
||||||
const auto scrollTop = _inner->scrollTopForPosition(position);
|
const auto scrollTop = _inner->scrollTopForPosition(position);
|
||||||
const auto scrolledBelow = scrollTop
|
const auto below = scrollTop
|
||||||
? (currentTop >= std::min(*scrollTop, _scroll->scrollTopMax()))
|
? (currentTop >= std::min(*scrollTop, _scroll->scrollTopMax()))
|
||||||
: _inner->isBelowPosition(position);
|
: _inner->isBelowPosition(position);
|
||||||
if (!scrolledBelow) {
|
if (below) {
|
||||||
|
_cornerButtons.calculateNextReplyReturn();
|
||||||
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -844,138 +817,52 @@ SendMenu::Type ScheduledWidget::sendMenuType() const {
|
||||||
: SendMenu::Type::Scheduled;
|
: SendMenu::Type::Scheduled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScheduledWidget::setupScrollDownButton() {
|
void ScheduledWidget::cornerButtonsShowAtPosition(
|
||||||
_scrollDown->setClickedCallback([=] {
|
Data::MessagePosition position) {
|
||||||
scrollDownClicked();
|
showAtPosition(position);
|
||||||
});
|
|
||||||
base::install_event_filter(_scrollDown, [=](not_null<QEvent*> event) {
|
|
||||||
if (event->type() != QEvent::Wheel) {
|
|
||||||
return base::EventFilterResult::Continue;
|
|
||||||
}
|
|
||||||
return _scroll->viewportEvent(event)
|
|
||||||
? base::EventFilterResult::Cancel
|
|
||||||
: base::EventFilterResult::Continue;
|
|
||||||
});
|
|
||||||
updateScrollDownVisibility();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScheduledWidget::scrollDownClicked() {
|
Dialogs::Entry *ScheduledWidget::cornerButtonsEntry() {
|
||||||
if (base::IsCtrlPressed()) {
|
return _history;
|
||||||
showAtEnd();
|
}
|
||||||
} else if (_replyReturn) {
|
|
||||||
showAtPosition(_replyReturn->position());
|
FullMsgId ScheduledWidget::cornerButtonsCurrentId() {
|
||||||
} else {
|
return {};
|
||||||
showAtEnd();
|
}
|
||||||
|
|
||||||
|
bool ScheduledWidget::cornerButtonsIgnoreVisibility() {
|
||||||
|
return animatingShow();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<bool> ScheduledWidget::cornerButtonsDownShown() {
|
||||||
|
if (_composeControls->isLockPresent()) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
const auto top = _scroll->scrollTop() + st::historyToDownShownAfter;
|
||||||
|
if (top < _scroll->scrollTopMax() || _cornerButtons.replyReturn()) {
|
||||||
|
return true;
|
||||||
|
} else if (_inner->loadedAtBottomKnown()) {
|
||||||
|
return !_inner->loadedAtBottom();
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScheduledWidget::showAtEnd() {
|
bool ScheduledWidget::cornerButtonsUnreadMayBeShown() {
|
||||||
showAtPosition(Data::MaxMessagePosition);
|
return _inner->loadedAtBottomKnown()
|
||||||
|
&& !_composeControls->isLockPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScheduledWidget::cornerButtonsHas(CornerButtonType type) {
|
||||||
|
return (type == CornerButtonType::Down);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScheduledWidget::showAtPosition(
|
void ScheduledWidget::showAtPosition(
|
||||||
Data::MessagePosition position,
|
Data::MessagePosition position,
|
||||||
HistoryItem *originItem) {
|
FullMsgId originId) {
|
||||||
if (showAtPositionNow(position, originItem)) {
|
_inner->showAtPosition(
|
||||||
if (const auto highlight = base::take(_highlightMessageId)) {
|
position,
|
||||||
_inner->highlightMessage(highlight);
|
anim::type::normal,
|
||||||
}
|
_cornerButtons.doneJumpFrom(position.fullId, originId));
|
||||||
} else {
|
|
||||||
_nextAnimatedScrollPosition = position;
|
|
||||||
_nextAnimatedScrollDelta = _inner->isBelowPosition(position)
|
|
||||||
? -_scroll->height()
|
|
||||||
: _inner->isAbovePosition(position)
|
|
||||||
? _scroll->height()
|
|
||||||
: 0;
|
|
||||||
auto memento = HistoryView::ListMemento(position);
|
|
||||||
_inner->restoreState(&memento);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ScheduledWidget::showAtPositionNow(
|
|
||||||
Data::MessagePosition position,
|
|
||||||
HistoryItem *originItem) {
|
|
||||||
if (const auto scrollTop = _inner->scrollTopForPosition(position)) {
|
|
||||||
while (_replyReturn && position.fullId.msg == _replyReturn->id) {
|
|
||||||
calculateNextReplyReturn();
|
|
||||||
}
|
|
||||||
const auto currentScrollTop = _scroll->scrollTop();
|
|
||||||
const auto wanted = std::clamp(
|
|
||||||
*scrollTop,
|
|
||||||
0,
|
|
||||||
_scroll->scrollTopMax());
|
|
||||||
const auto fullDelta = (wanted - currentScrollTop);
|
|
||||||
const auto limit = _scroll->height();
|
|
||||||
const auto scrollDelta = std::clamp(fullDelta, -limit, limit);
|
|
||||||
_inner->scrollTo(
|
|
||||||
wanted,
|
|
||||||
position,
|
|
||||||
scrollDelta,
|
|
||||||
(std::abs(fullDelta) > limit
|
|
||||||
? HistoryView::ListWidget::AnimatedScroll::Part
|
|
||||||
: HistoryView::ListWidget::AnimatedScroll::Full));
|
|
||||||
if (position != Data::MaxMessagePosition
|
|
||||||
&& position != Data::UnreadMessagePosition) {
|
|
||||||
_inner->highlightMessage(position.fullId);
|
|
||||||
}
|
|
||||||
if (originItem) {
|
|
||||||
pushReplyReturn(originItem);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScheduledWidget::updateScrollDownVisibility() {
|
|
||||||
if (animatingShow()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto scrollDownIsVisible = [&]() -> std::optional<bool> {
|
|
||||||
if (_composeControls->isLockPresent()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto top = _scroll->scrollTop() + st::historyToDownShownAfter;
|
|
||||||
if (top < _scroll->scrollTopMax()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (_inner->loadedAtBottomKnown()) {
|
|
||||||
return !_inner->loadedAtBottom();
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
};
|
|
||||||
const auto scrollDownIsShown = scrollDownIsVisible();
|
|
||||||
if (!scrollDownIsShown) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (_scrollDownIsShown != *scrollDownIsShown) {
|
|
||||||
_scrollDownIsShown = *scrollDownIsShown;
|
|
||||||
_scrollDownShown.start(
|
|
||||||
[=] { updateScrollDownPosition(); },
|
|
||||||
_scrollDownIsShown ? 0. : 1.,
|
|
||||||
_scrollDownIsShown ? 1. : 0.,
|
|
||||||
st::historyToDownDuration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScheduledWidget::updateScrollDownPosition() {
|
|
||||||
// _scrollDown is a child widget of _scroll, not me.
|
|
||||||
auto top = anim::interpolate(
|
|
||||||
0,
|
|
||||||
_scrollDown->height() + st::historyToDownPosition.y(),
|
|
||||||
_scrollDownShown.value(_scrollDownIsShown ? 1. : 0.));
|
|
||||||
_scrollDown->moveToRight(
|
|
||||||
st::historyToDownPosition.x(),
|
|
||||||
_scroll->height() - top);
|
|
||||||
auto shouldBeHidden = !_scrollDownIsShown && !_scrollDownShown.animating();
|
|
||||||
if (shouldBeHidden != _scrollDown->isHidden()) {
|
|
||||||
_scrollDown->setVisible(!shouldBeHidden);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScheduledWidget::scrollDownAnimationFinish() {
|
|
||||||
_scrollDownShown.stop();
|
|
||||||
updateScrollDownPosition();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScheduledWidget::updateAdaptiveLayout() {
|
void ScheduledWidget::updateAdaptiveLayout() {
|
||||||
|
@ -1092,7 +979,7 @@ void ScheduledWidget::updateControlsGeometry() {
|
||||||
_composeControls->move(0, bottom - controlsHeight);
|
_composeControls->move(0, bottom - controlsHeight);
|
||||||
_composeControls->setAutocompleteBoundingRect(_scroll->geometry());
|
_composeControls->setAutocompleteBoundingRect(_scroll->geometry());
|
||||||
|
|
||||||
updateScrollDownPosition();
|
_cornerButtons.updatePositions();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScheduledWidget::paintEvent(QPaintEvent *e) {
|
void ScheduledWidget::paintEvent(QPaintEvent *e) {
|
||||||
|
@ -1126,7 +1013,8 @@ void ScheduledWidget::updateInnerVisibleArea() {
|
||||||
}
|
}
|
||||||
const auto scrollTop = _scroll->scrollTop();
|
const auto scrollTop = _scroll->scrollTop();
|
||||||
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
|
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
|
||||||
updateScrollDownVisibility();
|
_cornerButtons.updateJumpDownVisibility();
|
||||||
|
_cornerButtons.updateUnreadThingsVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScheduledWidget::showAnimatedHook(
|
void ScheduledWidget::showAnimatedHook(
|
||||||
|
@ -1224,7 +1112,6 @@ void ScheduledWidget::highlightSingleNewMessage(
|
||||||
}
|
}
|
||||||
const auto newId = slice.ids[firstDifferent];
|
const auto newId = slice.ids[firstDifferent];
|
||||||
if (const auto item = session().data().message(newId)) {
|
if (const auto item = session().data().message(newId)) {
|
||||||
// _highlightMessageId = newId;
|
|
||||||
showAtPosition(item->position());
|
showAtPosition(item->position());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1267,7 +1154,7 @@ void ScheduledWidget::listMarkContentsRead(
|
||||||
|
|
||||||
MessagesBarData ScheduledWidget::listMessagesBar(
|
MessagesBarData ScheduledWidget::listMessagesBar(
|
||||||
const std::vector<not_null<Element*>> &elements) {
|
const std::vector<not_null<Element*>> &elements) {
|
||||||
return MessagesBarData();
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScheduledWidget::listContentRefreshed() {
|
void ScheduledWidget::listContentRefreshed() {
|
||||||
|
@ -1308,14 +1195,16 @@ bool ScheduledWidget::showMessage(
|
||||||
if (const auto origin = std::get_if<OriginMessage>(¶ms.origin)) {
|
if (const auto origin = std::get_if<OriginMessage>(¶ms.origin)) {
|
||||||
if (const auto returnTo = session().data().message(origin->id)) {
|
if (const auto returnTo = session().data().message(origin->id)) {
|
||||||
if (_inner->viewByPosition(returnTo->position())
|
if (_inner->viewByPosition(returnTo->position())
|
||||||
&& _replyReturn != returnTo) {
|
&& _cornerButtons.replyReturn() != returnTo) {
|
||||||
return returnTo;
|
return returnTo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}();
|
}();
|
||||||
showAtPosition(message->position(), originItem);
|
showAtPosition(
|
||||||
|
message->position(),
|
||||||
|
originItem ? originItem->fullId() : FullMsgId());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "window/section_widget.h"
|
#include "window/section_widget.h"
|
||||||
#include "window/section_memento.h"
|
#include "window/section_memento.h"
|
||||||
#include "history/view/history_view_list_widget.h"
|
#include "history/view/history_view_list_widget.h"
|
||||||
|
#include "history/view/history_view_corner_buttons.h"
|
||||||
#include "data/data_messages.h"
|
#include "data/data_messages.h"
|
||||||
|
|
||||||
class History;
|
class History;
|
||||||
|
@ -52,7 +53,8 @@ class StickerToast;
|
||||||
|
|
||||||
class ScheduledWidget final
|
class ScheduledWidget final
|
||||||
: public Window::SectionWidget
|
: public Window::SectionWidget
|
||||||
, private ListDelegate {
|
, private ListDelegate
|
||||||
|
, private CornerButtonsDelegate {
|
||||||
public:
|
public:
|
||||||
ScheduledWidget(
|
ScheduledWidget(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
|
@ -121,7 +123,8 @@ public:
|
||||||
ClickHandlerPtr listDateLink(not_null<Element*> view) override;
|
ClickHandlerPtr listDateLink(not_null<Element*> view) override;
|
||||||
bool listElementHideReply(not_null<const Element*> view) override;
|
bool listElementHideReply(not_null<const Element*> view) override;
|
||||||
bool listElementShownUnread(not_null<const Element*> view) override;
|
bool listElementShownUnread(not_null<const Element*> view) override;
|
||||||
bool listIsGoodForAroundPosition(not_null<const Element *> view) override;
|
bool listIsGoodForAroundPosition(
|
||||||
|
not_null<const Element *> view) override;
|
||||||
void listSendBotCommand(
|
void listSendBotCommand(
|
||||||
const QString &command,
|
const QString &command,
|
||||||
const FullMsgId &context) override;
|
const FullMsgId &context) override;
|
||||||
|
@ -133,6 +136,16 @@ public:
|
||||||
-> rpl::producer<Data::AllowedReactions> override;
|
-> rpl::producer<Data::AllowedReactions> override;
|
||||||
void listShowPremiumToast(not_null<DocumentData*> document) override;
|
void listShowPremiumToast(not_null<DocumentData*> document) override;
|
||||||
|
|
||||||
|
// CornerButtonsDelegate delegate.
|
||||||
|
void cornerButtonsShowAtPosition(
|
||||||
|
Data::MessagePosition position) override;
|
||||||
|
Dialogs::Entry *cornerButtonsEntry() override;
|
||||||
|
FullMsgId cornerButtonsCurrentId() override;
|
||||||
|
bool cornerButtonsIgnoreVisibility() override;
|
||||||
|
std::optional<bool> cornerButtonsDownShown() override;
|
||||||
|
bool cornerButtonsUnreadMayBeShown() override;
|
||||||
|
bool cornerButtonsHas(CornerButtonType type) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void resizeEvent(QResizeEvent *e) override;
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
void paintEvent(QPaintEvent *e) override;
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
@ -151,22 +164,12 @@ private:
|
||||||
void restoreState(not_null<ScheduledMemento*> memento);
|
void restoreState(not_null<ScheduledMemento*> memento);
|
||||||
void showAtPosition(
|
void showAtPosition(
|
||||||
Data::MessagePosition position,
|
Data::MessagePosition position,
|
||||||
HistoryItem *originItem = nullptr);
|
FullMsgId originId = {});
|
||||||
bool showAtPositionNow(
|
|
||||||
Data::MessagePosition position,
|
|
||||||
HistoryItem *originItem);
|
|
||||||
|
|
||||||
void setupComposeControls();
|
void setupComposeControls();
|
||||||
|
|
||||||
void setupDragArea();
|
void setupDragArea();
|
||||||
|
|
||||||
void setupScrollDownButton();
|
|
||||||
void scrollDownClicked();
|
|
||||||
void scrollDownAnimationFinish();
|
|
||||||
void updateScrollDownVisibility();
|
|
||||||
void updateScrollDownPosition();
|
|
||||||
void showAtEnd();
|
|
||||||
|
|
||||||
void confirmSendNowSelected();
|
void confirmSendNowSelected();
|
||||||
void confirmDeleteSelected();
|
void confirmDeleteSelected();
|
||||||
void clearSelected();
|
void clearSelected();
|
||||||
|
@ -190,8 +193,6 @@ private:
|
||||||
[[nodiscard]] SendMenu::Type sendMenuType() const;
|
[[nodiscard]] SendMenu::Type sendMenuType() const;
|
||||||
|
|
||||||
void pushReplyReturn(not_null<HistoryItem*> item);
|
void pushReplyReturn(not_null<HistoryItem*> item);
|
||||||
void computeCurrentReplyReturn();
|
|
||||||
void calculateNextReplyReturn();
|
|
||||||
void checkReplyReturns();
|
void checkReplyReturns();
|
||||||
|
|
||||||
void uploadFile(const QByteArray &fileContent, SendMediaType type);
|
void uploadFile(const QByteArray &fileContent, SendMediaType type);
|
||||||
|
@ -242,16 +243,7 @@ private:
|
||||||
|
|
||||||
std::unique_ptr<HistoryView::StickerToast> _stickerToast;
|
std::unique_ptr<HistoryView::StickerToast> _stickerToast;
|
||||||
|
|
||||||
std::vector<MsgId> _replyReturns;
|
CornerButtons _cornerButtons;
|
||||||
HistoryItem *_replyReturn = nullptr;
|
|
||||||
|
|
||||||
FullMsgId _highlightMessageId;
|
|
||||||
std::optional<Data::MessagePosition> _nextAnimatedScrollPosition;
|
|
||||||
int _nextAnimatedScrollDelta = 0;
|
|
||||||
|
|
||||||
Ui::Animations::Simple _scrollDownShown;
|
|
||||||
bool _scrollDownIsShown = false;
|
|
||||||
object_ptr<Ui::HistoryDownButton> _scrollDown;
|
|
||||||
|
|
||||||
Data::MessagesSlice _lastSlice;
|
Data::MessagesSlice _lastSlice;
|
||||||
bool _choosingAttach = false;
|
bool _choosingAttach = false;
|
||||||
|
|
Loading…
Add table
Reference in a new issue