mirror of https://github.com/procxx/kepka.git
Add scroll-to-down button to Feed.
This commit is contained in:
parent
b8614c60f9
commit
11671e85da
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
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 "core/event_filter.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
|
||||||
|
EventFilter::EventFilter(
|
||||||
|
not_null<QObject*> parent,
|
||||||
|
base::lambda<bool(not_null<QEvent*>)> filter)
|
||||||
|
: QObject(parent)
|
||||||
|
, _filter(std::move(filter)) {
|
||||||
|
parent->installEventFilter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EventFilter::eventFilter(QObject *watched, QEvent *event) {
|
||||||
|
return _filter(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
not_null<QObject*> InstallEventFilter(
|
||||||
|
not_null<QObject*> object,
|
||||||
|
base::lambda<bool(not_null<QEvent*>)> filter) {
|
||||||
|
return new EventFilter(object, std::move(filter));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Core
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
|
||||||
|
class EventFilter : public QObject {
|
||||||
|
public:
|
||||||
|
EventFilter(
|
||||||
|
not_null<QObject*> parent,
|
||||||
|
base::lambda<bool(not_null<QEvent*>)> filter);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool eventFilter(QObject *watched, QEvent *event);
|
||||||
|
|
||||||
|
private:
|
||||||
|
base::lambda<bool(not_null<QEvent*>)> _filter;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
not_null<QObject*> InstallEventFilter(
|
||||||
|
not_null<QObject*> object,
|
||||||
|
base::lambda<bool(not_null<QEvent*>)> filter);
|
||||||
|
|
||||||
|
} // namespace Core
|
|
@ -279,6 +279,12 @@ int Feed::unreadCount() const {
|
||||||
return _unreadCount ? *_unreadCount : 0;
|
return _unreadCount ? *_unreadCount : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<int> Feed::unreadCountValue() const {
|
||||||
|
return rpl::single(
|
||||||
|
unreadCount()
|
||||||
|
) | rpl::then(_unreadCountChanges.events());
|
||||||
|
}
|
||||||
|
|
||||||
bool Feed::unreadCountKnown() const {
|
bool Feed::unreadCountKnown() const {
|
||||||
return !!_unreadCount;
|
return !!_unreadCount;
|
||||||
}
|
}
|
||||||
|
@ -321,6 +327,8 @@ void Feed::setUnreadCounts(int unreadNonMutedCount, int unreadMutedCount) {
|
||||||
}
|
}
|
||||||
_unreadCount = unreadNonMutedCount + unreadMutedCount;
|
_unreadCount = unreadNonMutedCount + unreadMutedCount;
|
||||||
_unreadMutedCount = unreadMutedCount;
|
_unreadMutedCount = unreadMutedCount;
|
||||||
|
|
||||||
|
_unreadCountChanges.fire(unreadCount());
|
||||||
updateChatListEntry();
|
updateChatListEntry();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,7 +341,7 @@ void Feed::setUnreadPosition(const MessagePosition &position) {
|
||||||
void Feed::unreadCountChanged(
|
void Feed::unreadCountChanged(
|
||||||
base::optional<int> unreadCountDelta,
|
base::optional<int> unreadCountDelta,
|
||||||
int mutedCountDelta) {
|
int mutedCountDelta) {
|
||||||
if (!unreadCountKnown()) {
|
if (!unreadCountKnown() || (unreadCountDelta && !*unreadCountDelta)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (unreadCountDelta) {
|
if (unreadCountDelta) {
|
||||||
|
@ -342,6 +350,8 @@ void Feed::unreadCountChanged(
|
||||||
_unreadMutedCount + mutedCountDelta,
|
_unreadMutedCount + mutedCountDelta,
|
||||||
0,
|
0,
|
||||||
*_unreadCount);
|
*_unreadCount);
|
||||||
|
|
||||||
|
_unreadCountChanges.fire(unreadCount());
|
||||||
updateChatListEntry();
|
updateChatListEntry();
|
||||||
} else {
|
} else {
|
||||||
// _parent->session().api().requestFeedDialogsEntries(this);
|
// _parent->session().api().requestFeedDialogsEntries(this);
|
||||||
|
|
|
@ -50,6 +50,7 @@ public:
|
||||||
void unreadCountChanged(
|
void unreadCountChanged(
|
||||||
base::optional<int> unreadCountDelta,
|
base::optional<int> unreadCountDelta,
|
||||||
int mutedCountDelta);
|
int mutedCountDelta);
|
||||||
|
rpl::producer<int> unreadCountValue() const;
|
||||||
MessagePosition unreadPosition() const;
|
MessagePosition unreadPosition() const;
|
||||||
rpl::producer<MessagePosition> unreadPositionChanges() const;
|
rpl::producer<MessagePosition> unreadPositionChanges() const;
|
||||||
|
|
||||||
|
@ -98,6 +99,7 @@ private:
|
||||||
|
|
||||||
rpl::variable<MessagePosition> _unreadPosition;
|
rpl::variable<MessagePosition> _unreadPosition;
|
||||||
base::optional<int> _unreadCount;
|
base::optional<int> _unreadCount;
|
||||||
|
rpl::event_stream<int> _unreadCountChanges;
|
||||||
int _unreadMutedCount = 0;
|
int _unreadMutedCount = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -77,12 +77,18 @@ struct MessagesRange {
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr auto MaxDate = std::numeric_limits<TimeId>::max();
|
constexpr auto MaxDate = std::numeric_limits<TimeId>::max();
|
||||||
constexpr auto MinMessagePosition = MessagePosition(TimeId(0), FullMsgId());
|
constexpr auto MinMessagePosition = MessagePosition(
|
||||||
constexpr auto MaxMessagePosition = MessagePosition(MaxDate, FullMsgId());
|
TimeId(0),
|
||||||
|
FullMsgId(0, 1));
|
||||||
|
constexpr auto MaxMessagePosition = MessagePosition(
|
||||||
|
MaxDate,
|
||||||
|
FullMsgId(0, ServerMaxMsgId - 1));
|
||||||
constexpr auto FullMessagesRange = MessagesRange(
|
constexpr auto FullMessagesRange = MessagesRange(
|
||||||
MinMessagePosition,
|
MinMessagePosition,
|
||||||
MaxMessagePosition);
|
MaxMessagePosition);
|
||||||
constexpr auto UnreadMessagePosition = MinMessagePosition;
|
constexpr auto UnreadMessagePosition = MessagePosition(
|
||||||
|
TimeId(0),
|
||||||
|
FullMsgId(0, 0));;
|
||||||
|
|
||||||
struct MessagesSlice {
|
struct MessagesSlice {
|
||||||
std::vector<FullMsgId> ids;
|
std::vector<FullMsgId> ids;
|
||||||
|
|
|
@ -1833,15 +1833,23 @@ void DialogsInner::peerSearchReceived(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto alreadyAdded = [&](not_null<PeerData*> peer) {
|
||||||
|
for (const auto &row : _filterResults) {
|
||||||
|
if (const auto history = row->history()) {
|
||||||
|
if (history->peer == peer) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
_peerSearchQuery = query.toLower().trimmed();
|
_peerSearchQuery = query.toLower().trimmed();
|
||||||
_peerSearchResults.clear();
|
_peerSearchResults.clear();
|
||||||
_peerSearchResults.reserve(result.size());
|
_peerSearchResults.reserve(result.size());
|
||||||
for (const auto &mtpPeer : my) {
|
for (const auto &mtpPeer : my) {
|
||||||
if (const auto peer = App::peerLoaded(peerFromMTP(mtpPeer))) {
|
if (const auto peer = App::peerLoaded(peerFromMTP(mtpPeer))) {
|
||||||
if (const auto history = App::historyLoaded(peer)) {
|
if (alreadyAdded(peer)) {
|
||||||
if (history->inChatList(Dialogs::Mode::All)) {
|
continue;
|
||||||
continue; // skip existing chats
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const auto prev = nullptr, next = nullptr;
|
const auto prev = nullptr, next = nullptr;
|
||||||
const auto position = 0;
|
const auto position = 0;
|
||||||
|
|
|
@ -13,11 +13,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/view/history_view_message.h"
|
#include "history/view/history_view_message.h"
|
||||||
#include "history/view/history_view_service_message.h"
|
#include "history/view/history_view_service_message.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
|
#include "core/event_filter.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/shadow.h"
|
#include "ui/widgets/shadow.h"
|
||||||
#include "ui/widgets/scroll_area.h"
|
#include "ui/widgets/scroll_area.h"
|
||||||
#include "ui/widgets/popup_menu.h"
|
#include "ui/widgets/popup_menu.h"
|
||||||
|
#include "ui/special_buttons.h"
|
||||||
#include "boxes/confirm_box.h"
|
#include "boxes/confirm_box.h"
|
||||||
#include "window/window_controller.h"
|
#include "window/window_controller.h"
|
||||||
#include "window/window_peer_menu.h"
|
#include "window/window_peer_menu.h"
|
||||||
|
@ -68,7 +70,8 @@ Widget::Widget(
|
||||||
, _showNext(
|
, _showNext(
|
||||||
this,
|
this,
|
||||||
lang(lng_feed_show_next).toUpper(),
|
lang(lng_feed_show_next).toUpper(),
|
||||||
st::historyComposeButton) {
|
st::historyComposeButton)
|
||||||
|
, _scrollDown(_scroll, st::historyToDown) {
|
||||||
_topBar->setActiveChat(_feed);
|
_topBar->setActiveChat(_feed);
|
||||||
|
|
||||||
_topBar->move(0, 0);
|
_topBar->move(0, 0);
|
||||||
|
@ -125,6 +128,107 @@ Widget::Widget(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
crl::on_main(this, [=] { checkForSingleChannelFeed(); });
|
crl::on_main(this, [=] { checkForSingleChannelFeed(); });
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
|
setupScrollDownButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Widget::setupScrollDownButton() {
|
||||||
|
_scrollDown->setClickedCallback([=] {
|
||||||
|
scrollDownClicked();
|
||||||
|
});
|
||||||
|
Core::InstallEventFilter(_scrollDown, [=](not_null<QEvent*> event) {
|
||||||
|
if (event->type() == QEvent::Wheel) {
|
||||||
|
return _scroll->viewportEvent(event);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
updateScrollDownVisibility();
|
||||||
|
_feed->unreadCountValue(
|
||||||
|
) | rpl::start_with_next([=](int count) {
|
||||||
|
_scrollDown->setUnreadCount(count);
|
||||||
|
}, _scrollDown->lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Widget::scrollDownClicked() {
|
||||||
|
showAtPosition(Data::MaxMessagePosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Widget::showAtPosition(Data::MessagePosition position) {
|
||||||
|
if (!showAtPositionNow(position)) {
|
||||||
|
_nextAnimatedScrollPosition = position;
|
||||||
|
_nextAnimatedScrollDelta = _inner->isBelowPosition(position)
|
||||||
|
? -_scroll->height()
|
||||||
|
: _inner->isAbovePosition(position)
|
||||||
|
? _scroll->height()
|
||||||
|
: 0;
|
||||||
|
auto memento = HistoryView::ListMemento(position);
|
||||||
|
_inner->restoreState(&memento);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Widget::showAtPositionNow(Data::MessagePosition position) {
|
||||||
|
if (const auto scrollTop = _inner->scrollTopForPosition(position)) {
|
||||||
|
const auto currentScrollTop = _scroll->scrollTop();
|
||||||
|
const auto wanted = snap(*scrollTop, 0, _scroll->scrollTopMax());
|
||||||
|
const auto fullDelta = (wanted - currentScrollTop);
|
||||||
|
const auto limit = _scroll->height();
|
||||||
|
const auto scrollDelta = snap(fullDelta, -limit, limit);
|
||||||
|
_inner->animatedScrollTo(
|
||||||
|
wanted,
|
||||||
|
position,
|
||||||
|
scrollDelta,
|
||||||
|
(std::abs(fullDelta) > limit
|
||||||
|
? HistoryView::ListWidget::AnimatedScroll::Part
|
||||||
|
: HistoryView::ListWidget::AnimatedScroll::Full));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Widget::updateScrollDownVisibility() {
|
||||||
|
if (animating() || !_inner->loadedAtBottomKnown()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto scrollDownIsVisible = [&] {
|
||||||
|
if (!_inner->loadedAtBottom()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const auto top = _scroll->scrollTop() + st::historyToDownShownAfter;
|
||||||
|
if (top < _scroll->scrollTopMax()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
auto scrollDownIsShown = scrollDownIsVisible();
|
||||||
|
if (_scrollDownIsShown != scrollDownIsShown) {
|
||||||
|
_scrollDownIsShown = scrollDownIsShown;
|
||||||
|
_scrollDownShown.start(
|
||||||
|
[=] { updateScrollDownPosition(); },
|
||||||
|
_scrollDownIsShown ? 0. : 1.,
|
||||||
|
_scrollDownIsShown ? 1. : 0.,
|
||||||
|
st::historyToDownDuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Widget::updateScrollDownPosition() {
|
||||||
|
// _scrollDown is a child widget of _scroll, not me.
|
||||||
|
auto top = anim::interpolate(
|
||||||
|
0,
|
||||||
|
_scrollDown->height() + st::historyToDownPosition.y(),
|
||||||
|
_scrollDownShown.current(_scrollDownIsShown ? 1. : 0.));
|
||||||
|
_scrollDown->moveToRight(
|
||||||
|
st::historyToDownPosition.x(),
|
||||||
|
_scroll->height() - top);
|
||||||
|
auto shouldBeHidden = !_scrollDownIsShown && !_scrollDownShown.animating();
|
||||||
|
if (shouldBeHidden != _scrollDown->isHidden()) {
|
||||||
|
_scrollDown->setVisible(!shouldBeHidden);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Widget::scrollDownAnimationFinish() {
|
||||||
|
_scrollDownShown.finish();
|
||||||
|
updateScrollDownPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::checkForSingleChannelFeed() {
|
void Widget::checkForSingleChannelFeed() {
|
||||||
|
@ -255,7 +359,7 @@ void Widget::listVisibleItemsChanged(HistoryItemsList &&items) {
|
||||||
base::optional<int> Widget::listUnreadBarView(
|
base::optional<int> Widget::listUnreadBarView(
|
||||||
const std::vector<not_null<Element*>> &elements) {
|
const std::vector<not_null<Element*>> &elements) {
|
||||||
const auto position = _feed->unreadPosition();
|
const auto position = _feed->unreadPosition();
|
||||||
if (!position || elements.empty()) {
|
if (!position || elements.empty() || !_feed->unreadCount()) {
|
||||||
return base::none;
|
return base::none;
|
||||||
}
|
}
|
||||||
const auto minimal = ranges::upper_bound(
|
const auto minimal = ranges::upper_bound(
|
||||||
|
@ -276,6 +380,21 @@ base::optional<int> Widget::listUnreadBarView(
|
||||||
return base::make_optional(int(minimal - begin(elements)));
|
return base::make_optional(int(minimal - begin(elements)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Widget::listContentRefreshed() {
|
||||||
|
if (!_nextAnimatedScrollPosition) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto position = *base::take(_nextAnimatedScrollPosition);
|
||||||
|
if (const auto scrollTop = _inner->scrollTopForPosition(position)) {
|
||||||
|
const auto wanted = snap(*scrollTop, 0, _scroll->scrollTopMax());
|
||||||
|
_inner->animatedScrollTo(
|
||||||
|
wanted,
|
||||||
|
position,
|
||||||
|
_nextAnimatedScrollDelta,
|
||||||
|
HistoryView::ListWidget::AnimatedScroll::Part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<Window::SectionMemento> Widget::createMemento() {
|
std::unique_ptr<Window::SectionMemento> Widget::createMemento() {
|
||||||
auto result = std::make_unique<Memento>(_feed);
|
auto result = std::make_unique<Memento>(_feed);
|
||||||
saveState(result.get());
|
saveState(result.get());
|
||||||
|
@ -330,6 +449,8 @@ void Widget::updateControlsGeometry() {
|
||||||
}
|
}
|
||||||
updateInnerVisibleArea();
|
updateInnerVisibleArea();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateScrollDownPosition();
|
||||||
const auto fullWidthButtonRect = myrtlrect(
|
const auto fullWidthButtonRect = myrtlrect(
|
||||||
0,
|
0,
|
||||||
bottom - _showNext->height(),
|
bottom - _showNext->height(),
|
||||||
|
@ -350,8 +471,8 @@ void Widget::paintEvent(QPaintEvent *e) {
|
||||||
// updateListSize();
|
// updateListSize();
|
||||||
//}
|
//}
|
||||||
|
|
||||||
//auto ms = getms();
|
const auto ms = getms();
|
||||||
//_historyDownShown.step(ms);
|
_scrollDownShown.step(ms);
|
||||||
|
|
||||||
SectionWidget::PaintBackground(this, e);
|
SectionWidget::PaintBackground(this, e);
|
||||||
}
|
}
|
||||||
|
@ -366,7 +487,7 @@ void Widget::onScroll() {
|
||||||
void Widget::updateInnerVisibleArea() {
|
void Widget::updateInnerVisibleArea() {
|
||||||
const auto scrollTop = _scroll->scrollTop();
|
const auto scrollTop = _scroll->scrollTop();
|
||||||
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
|
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
|
||||||
|
updateScrollDownVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::showAnimatedHook(
|
void Widget::showAnimatedHook(
|
||||||
|
|
|
@ -16,6 +16,7 @@ namespace Ui {
|
||||||
class ScrollArea;
|
class ScrollArea;
|
||||||
class PlainShadow;
|
class PlainShadow;
|
||||||
class FlatButton;
|
class FlatButton;
|
||||||
|
class HistoryDownButton;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
|
@ -81,6 +82,7 @@ public:
|
||||||
void listVisibleItemsChanged(HistoryItemsList &&items) override;
|
void listVisibleItemsChanged(HistoryItemsList &&items) override;
|
||||||
base::optional<int> listUnreadBarView(
|
base::optional<int> listUnreadBarView(
|
||||||
const std::vector<not_null<Element*>> &elements) override;
|
const std::vector<not_null<Element*>> &elements) override;
|
||||||
|
void listContentRefreshed() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void resizeEvent(QResizeEvent *e) override;
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
|
@ -99,6 +101,14 @@ private:
|
||||||
void updateAdaptiveLayout();
|
void updateAdaptiveLayout();
|
||||||
void saveState(not_null<Memento*> memento);
|
void saveState(not_null<Memento*> memento);
|
||||||
void restoreState(not_null<Memento*> memento);
|
void restoreState(not_null<Memento*> memento);
|
||||||
|
void showAtPosition(Data::MessagePosition position);
|
||||||
|
bool showAtPositionNow(Data::MessagePosition position);
|
||||||
|
|
||||||
|
void setupScrollDownButton();
|
||||||
|
void scrollDownClicked();
|
||||||
|
void scrollDownAnimationFinish();
|
||||||
|
void updateScrollDownVisibility();
|
||||||
|
void updateScrollDownPosition();
|
||||||
|
|
||||||
void forwardSelected();
|
void forwardSelected();
|
||||||
void confirmDeleteSelected();
|
void confirmDeleteSelected();
|
||||||
|
@ -113,6 +123,14 @@ private:
|
||||||
bool _skipScrollEvent = false;
|
bool _skipScrollEvent = false;
|
||||||
bool _undefinedAroundPosition = false;
|
bool _undefinedAroundPosition = false;
|
||||||
|
|
||||||
|
base::optional<Data::MessagePosition> _nextAnimatedScrollPosition;
|
||||||
|
int _nextAnimatedScrollDelta = 0;
|
||||||
|
|
||||||
|
Animation _scrollDownShown;
|
||||||
|
bool _scrollDownIsShown = false;
|
||||||
|
object_ptr<Ui::HistoryDownButton> _scrollDown;
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Memento : public Window::SectionMemento {
|
class Memento : public Window::SectionMemento {
|
||||||
|
|
|
@ -324,6 +324,75 @@ void ListWidget::refreshRows() {
|
||||||
checkUnreadBarCreation();
|
checkUnreadBarCreation();
|
||||||
restoreScrollState();
|
restoreScrollState();
|
||||||
mouseActionUpdate(QCursor::pos());
|
mouseActionUpdate(QCursor::pos());
|
||||||
|
_delegate->listContentRefreshed();
|
||||||
|
}
|
||||||
|
|
||||||
|
base::optional<int> ListWidget::scrollTopForPosition(
|
||||||
|
Data::MessagePosition position) const {
|
||||||
|
if (position == Data::MaxMessagePosition) {
|
||||||
|
if (loadedAtBottom()) {
|
||||||
|
return height();
|
||||||
|
}
|
||||||
|
return base::none;
|
||||||
|
}
|
||||||
|
// #TODO showAtPosition
|
||||||
|
return base::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListWidget::animatedScrollTo(
|
||||||
|
int scrollTop,
|
||||||
|
Data::MessagePosition attachPosition,
|
||||||
|
int delta,
|
||||||
|
AnimatedScroll type) {
|
||||||
|
_scrollToAnimation.finish();
|
||||||
|
if (!delta || _items.empty()) {
|
||||||
|
_delegate->listScrollTo(scrollTop);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto index = findNearestItem(attachPosition);
|
||||||
|
Assert(index >= 0 && index < int(_items.size()));
|
||||||
|
const auto attachTo = _items[index];
|
||||||
|
const auto attachToId = attachTo->data()->fullId();
|
||||||
|
const auto transition = (type == AnimatedScroll::Full)
|
||||||
|
? anim::sineInOut
|
||||||
|
: anim::easeOutCubic;
|
||||||
|
const auto initial = scrollTop - delta;
|
||||||
|
_delegate->listScrollTo(initial);
|
||||||
|
|
||||||
|
const auto attachToTop = itemTop(attachTo);
|
||||||
|
const auto relativeStart = initial - attachToTop;
|
||||||
|
const auto relativeFinish = scrollTop - attachToTop;
|
||||||
|
_scrollToAnimation.start(
|
||||||
|
[=] { scrollToAnimationCallback(attachToId); },
|
||||||
|
relativeStart,
|
||||||
|
relativeFinish,
|
||||||
|
st::slideDuration,
|
||||||
|
transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListWidget::scrollToAnimationCallback(FullMsgId attachToId) {
|
||||||
|
const auto attachTo = App::histItemById(attachToId);
|
||||||
|
const auto attachToView = viewForItem(attachTo);
|
||||||
|
if (!attachToView) {
|
||||||
|
_scrollToAnimation.finish();
|
||||||
|
} else {
|
||||||
|
const auto current = int(std::round(_scrollToAnimation.current()));
|
||||||
|
_delegate->listScrollTo(itemTop(attachToView) + current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ListWidget::isAbovePosition(Data::MessagePosition position) const {
|
||||||
|
if (_items.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return _items.back()->data()->position() < position;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ListWidget::isBelowPosition(Data::MessagePosition position) const {
|
||||||
|
if (_items.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return _items.front()->data()->position() > position;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListWidget::checkUnreadBarCreation() {
|
void ListWidget::checkUnreadBarCreation() {
|
||||||
|
@ -838,6 +907,22 @@ void ListWidget::setTextSelection(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ListWidget::loadedAtTopKnown() const {
|
||||||
|
return !!_slice.skippedBefore;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ListWidget::loadedAtTop() const {
|
||||||
|
return _slice.skippedBefore && (*_slice.skippedBefore == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ListWidget::loadedAtBottomKnown() const {
|
||||||
|
return !!_slice.skippedAfter;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ListWidget::loadedAtBottom() const {
|
||||||
|
return _slice.skippedAfter && (*_slice.skippedAfter == 0);
|
||||||
|
}
|
||||||
|
|
||||||
int ListWidget::itemMinimalHeight() const {
|
int ListWidget::itemMinimalHeight() const {
|
||||||
return st::msgMarginTopAttached
|
return st::msgMarginTopAttached
|
||||||
+ st::msgPhotoSize
|
+ st::msgPhotoSize
|
||||||
|
|
|
@ -64,6 +64,7 @@ public:
|
||||||
virtual void listVisibleItemsChanged(HistoryItemsList &&items) = 0;
|
virtual void listVisibleItemsChanged(HistoryItemsList &&items) = 0;
|
||||||
virtual base::optional<int> listUnreadBarView(
|
virtual base::optional<int> listUnreadBarView(
|
||||||
const std::vector<not_null<Element*>> &elements) = 0;
|
const std::vector<not_null<Element*>> &elements) = 0;
|
||||||
|
virtual void listContentRefreshed() = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -134,6 +135,19 @@ public:
|
||||||
|
|
||||||
void saveState(not_null<ListMemento*> memento);
|
void saveState(not_null<ListMemento*> memento);
|
||||||
void restoreState(not_null<ListMemento*> memento);
|
void restoreState(not_null<ListMemento*> memento);
|
||||||
|
base::optional<int> scrollTopForPosition(
|
||||||
|
Data::MessagePosition position) const;
|
||||||
|
enum class AnimatedScroll {
|
||||||
|
Full,
|
||||||
|
Part,
|
||||||
|
};
|
||||||
|
void animatedScrollTo(
|
||||||
|
int scrollTop,
|
||||||
|
Data::MessagePosition attachPosition,
|
||||||
|
int delta,
|
||||||
|
AnimatedScroll type);
|
||||||
|
bool isAbovePosition(Data::MessagePosition position) const;
|
||||||
|
bool isBelowPosition(Data::MessagePosition position) const;
|
||||||
|
|
||||||
TextWithEntities getSelectedText() const;
|
TextWithEntities getSelectedText() const;
|
||||||
MessageIdsList getSelectedItems() const;
|
MessageIdsList getSelectedItems() const;
|
||||||
|
@ -141,6 +155,11 @@ public:
|
||||||
void selectItem(not_null<HistoryItem*> item);
|
void selectItem(not_null<HistoryItem*> item);
|
||||||
void selectItemAsGroup(not_null<HistoryItem*> item);
|
void selectItemAsGroup(not_null<HistoryItem*> item);
|
||||||
|
|
||||||
|
bool loadedAtTopKnown() const;
|
||||||
|
bool loadedAtTop() const;
|
||||||
|
bool loadedAtBottomKnown() const;
|
||||||
|
bool loadedAtBottom() const;
|
||||||
|
|
||||||
// AbstractTooltipShower interface
|
// AbstractTooltipShower interface
|
||||||
QString tooltipText() const override;
|
QString tooltipText() const override;
|
||||||
QPoint tooltipPos() const override;
|
QPoint tooltipPos() const override;
|
||||||
|
@ -355,6 +374,7 @@ private:
|
||||||
not_null<const Element*> view) const;
|
not_null<const Element*> view) const;
|
||||||
void checkUnreadBarCreation();
|
void checkUnreadBarCreation();
|
||||||
void applyUpdatedScrollState();
|
void applyUpdatedScrollState();
|
||||||
|
void scrollToAnimationCallback(FullMsgId attachToId);
|
||||||
|
|
||||||
// This function finds all history items that are displayed and calls template method
|
// This function finds all history items that are displayed and calls template method
|
||||||
// for each found message (in given direction) in the passed history with passed top offset.
|
// for each found message (in given direction) in the passed history with passed top offset.
|
||||||
|
@ -385,6 +405,7 @@ private:
|
||||||
not_null<ListDelegate*> _delegate;
|
not_null<ListDelegate*> _delegate;
|
||||||
not_null<Window::Controller*> _controller;
|
not_null<Window::Controller*> _controller;
|
||||||
Data::MessagePosition _aroundPosition;
|
Data::MessagePosition _aroundPosition;
|
||||||
|
Data::MessagePosition _shownAtPosition;
|
||||||
Context _context;
|
Context _context;
|
||||||
int _aroundIndex = -1;
|
int _aroundIndex = -1;
|
||||||
int _idsLimit = kMinimalIdsLimit;
|
int _idsLimit = kMinimalIdsLimit;
|
||||||
|
@ -405,6 +426,7 @@ private:
|
||||||
Element *_visibleTopItem = nullptr;
|
Element *_visibleTopItem = nullptr;
|
||||||
int _visibleTopFromItem = 0;
|
int _visibleTopFromItem = 0;
|
||||||
ScrollTopState _scrollTopState;
|
ScrollTopState _scrollTopState;
|
||||||
|
Animation _scrollToAnimation;
|
||||||
|
|
||||||
bool _scrollDateShown = false;
|
bool _scrollDateShown = false;
|
||||||
Animation _scrollDateOpacity;
|
Animation _scrollDateOpacity;
|
||||||
|
|
|
@ -147,6 +147,8 @@
|
||||||
<(src_loc)/core/crash_report_window.h
|
<(src_loc)/core/crash_report_window.h
|
||||||
<(src_loc)/core/crash_reports.cpp
|
<(src_loc)/core/crash_reports.cpp
|
||||||
<(src_loc)/core/crash_reports.h
|
<(src_loc)/core/crash_reports.h
|
||||||
|
<(src_loc)/core/event_filter.cpp
|
||||||
|
<(src_loc)/core/event_filter.h
|
||||||
<(src_loc)/core/file_utilities.cpp
|
<(src_loc)/core/file_utilities.cpp
|
||||||
<(src_loc)/core/file_utilities.h
|
<(src_loc)/core/file_utilities.h
|
||||||
<(src_loc)/core/launcher.cpp
|
<(src_loc)/core/launcher.cpp
|
||||||
|
|
Loading…
Reference in New Issue