Working stories in overscroll.

This commit is contained in:
John Preston 2023-07-11 11:15:32 +04:00
parent e0c10e7cc2
commit 85795aa376
10 changed files with 232 additions and 264 deletions

View File

@ -246,7 +246,7 @@ dialogsSearchForNarrowFilters: IconButton(dialogsMenuToggle) {
dialogsFilter: InputField(defaultInputField) {
textBg: filterInputInactiveBg;
textBgActive: filterInputActiveBg;
textMargins: margins(12px, 7px, 30px, 3px);
textMargins: margins(12px, 8px, 30px, 5px);
placeholderFg: placeholderFg;
placeholderFgActive: placeholderFgActive;
@ -257,16 +257,16 @@ dialogsFilter: InputField(defaultInputField) {
placeholderFont: normalFont;
borderFg: filterInputInactiveBg;
borderFgActive: filterInputBorderFg;
borderFgActive: windowBgRipple;
borderFgError: activeLineFgError;
border: 2px;
borderActive: 2px;
borderRadius: roundRadiusSmall;
borderRadius: 18px;
font: normalFont;
heightMin: 32px;
heightMin: 35px;
}
dialogsCancelSearchInPeer: IconButton(dialogsMenuToggle) {
icon: icon {{ "dialogs/dialogs_cancel_search", dialogsMenuIconFg }};
@ -511,8 +511,9 @@ DialogsStoriesList {
dialogsStories: DialogsStories {
left: 4px;
height: 35px;
photo: 24px;
photoLeft: 10px;
photo: 21px;
photoTop: 4px;
photoLeft: 4px;
shift: 16px;
lineTwice: 3px;
lineReadTwice: 0px;

View File

@ -141,13 +141,6 @@ InnerWidget::InnerWidget(
rpl::producer<ChildListShown> childListShown)
: RpWidget(parent)
, _controller(controller)
, _stories(std::make_unique<Stories::List>(
this,
st::dialogsStoriesList,
Stories::ContentForSession(
&controller->session(),
Data::StorySourcesList::NotHidden),
[=] { return _stories->height() - _visibleTop; }))
, _shownList(controller->session().data().chatsList()->indexed())
, _st(&st::defaultDialogRow)
, _pinnedShiftAnimation([=](crl::time now) {
@ -328,37 +321,6 @@ InnerWidget::InnerWidget(
switchToFilter(filterId);
}, lifetime());
_stories->heightValue(
) | rpl::filter([=] {
return (_viewportHeight > 0) && (defaultScrollTop() > _visibleTop);
}) | rpl::start_with_next([=] {
refreshForDefaultScroll();
jumpToTop();
}, lifetime());
_stories->entered(
) | rpl::start_with_next([=] {
clearSelection();
}, lifetime());
_stories->clicks(
) | rpl::start_with_next([=](uint64 id) {
_controller->openPeerStories(
PeerId(int64(id)),
Data::StorySourcesList::NotHidden);
}, lifetime());
_stories->showMenuRequests(
) | rpl::start_with_next([=](const Stories::ShowMenuRequest &request) {
FillSourceMenu(_controller, request);
}, lifetime());
_stories->loadMoreRequests(
) | rpl::start_with_next([=] {
session().data().stories().loadMore(
Data::StorySourcesList::NotHidden);
}, lifetime());
session().data().stories().incrementPreloadingMainSources();
handleChatListEntryRefreshes();
@ -450,14 +412,8 @@ int InnerWidget::skipTopHeight() const {
: 0;
}
bool InnerWidget::storiesShown() const {
return (_state == WidgetState::Default)
&& !_openedFolder
&& !_openedForum;
}
int InnerWidget::collapsedRowsOffset() const {
return storiesShown() ? _stories->height() : 0;
return 0;
}
int InnerWidget::dialogsOffset() const {
@ -466,22 +422,6 @@ int InnerWidget::dialogsOffset() const {
- skipTopHeight();
}
rpl::producer<bool> InnerWidget::storiesExpandedRequests() const {
return rpl::merge(
_stories->toggleExpandedRequests(),
_storiesExpandedRequests.events());
}
void InnerWidget::setTouchScrollActive(bool active) {
_stories->setTouchScrollActive(active);
}
int InnerWidget::defaultScrollTop() const {
return storiesShown()
? std::max(_stories->height() - st::dialogsStories.height, 0)
: 0;
}
int InnerWidget::fixedOnTopCount() const {
auto result = 0;
for (const auto &row : *_shownList) {
@ -564,7 +504,6 @@ void InnerWidget::changeOpenedFolder(Data::Folder *folder) {
stopReorderPinned();
clearSelection();
_openedFolder = folder;
_stories->setVisible(storiesShown());
refreshShownList();
refreshWithCollapsedRows(true);
if (_loadMoreCallback) {
@ -591,7 +530,6 @@ void InnerWidget::changeOpenedForum(Data::Forum *forum) {
}
_openedForum = forum;
_st = forum ? &st::forumTopicRow : &st::defaultDialogRow;
_stories->setVisible(storiesShown());
refreshShownList();
_openedForumLifetime.destroy();
@ -642,7 +580,6 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
.paused = videoPaused,
.narrow = (fullWidth < st::columnMinimalWidthLeft / 2),
};
_stories->setBgOverride(context.currentBg);
const auto fillGuard = gsl::finally([&] {
// We translate painter down, but it'll be cropped below rect.
p.fillRect(rect(), context.currentBg);
@ -1759,13 +1696,6 @@ void InnerWidget::mousePressReleased(
}
}
void InnerWidget::setViewportHeight(int viewportHeight) {
if (_viewportHeight != viewportHeight) {
_viewportHeight = viewportHeight;
refreshForDefaultScroll();
}
}
void InnerWidget::setCollapsedPressed(int pressed) {
if (_collapsedPressed != pressed) {
if (_collapsedPressed >= 0) {
@ -1836,7 +1766,6 @@ void InnerWidget::setSearchedPressed(int pressed) {
}
void InnerWidget::resizeEvent(QResizeEvent *e) {
_stories->resizeToWidth(width());
resizeEmptyLabel();
moveCancelSearchButtons();
}
@ -2597,7 +2526,6 @@ void InnerWidget::visibleTopBottomUpdated(
int visibleBottom) {
_visibleTop = visibleTop;
_visibleBottom = visibleBottom;
_stories->update();
preloadRowsData();
const auto loadTill = _visibleTop
+ PreloadHeightsCount * (_visibleBottom - _visibleTop);
@ -2793,12 +2721,6 @@ void InnerWidget::editOpenedFilter() {
}
}
void InnerWidget::refreshForDefaultScroll() {
if (height() < defaultScrollTop() + _viewportHeight) {
refresh();
}
}
void InnerWidget::refresh(bool toTop) {
if (!_geometryInited) {
return;
@ -2820,9 +2742,6 @@ void InnerWidget::refresh(bool toTop) {
h = searchedOffset() + (_searchResults.size() * _st->height);
}
}
if (const auto storiesSkip = defaultScrollTop()) {
accumulate_max(h, storiesSkip + _viewportHeight);
}
resize(width(), h);
if (toTop) {
stopReorderPinned();
@ -3033,10 +2952,7 @@ void InnerWidget::clearFilter() {
}
void InnerWidget::setState(WidgetState state) {
if (_state != state) {
_state = state;
_stories->setVisible(storiesShown());
}
_state = state;
}
void InnerWidget::selectSkip(int32 direction) {
@ -3331,8 +3247,7 @@ void InnerWidget::switchToFilter(FilterId filterId) {
}
void InnerWidget::jumpToTop() {
const auto to = defaultScrollTop();
_mustScrollTo.fire({ to, -1 });
_mustScrollTo.fire({ 0, -1 });
}
void InnerWidget::saveChatsFilterScrollState(FilterId filterId) {
@ -3342,8 +3257,7 @@ void InnerWidget::saveChatsFilterScrollState(FilterId filterId) {
void InnerWidget::restoreChatsFilterScrollState(FilterId filterId) {
const auto it = _chatsFilterScrollStates.find(filterId);
if (it != end(_chatsFilterScrollStates)) {
const auto top = std::max(it->second, defaultScrollTop());
_mustScrollTo.fire({ top, -1 });
_mustScrollTo.fire({ std::max(it->second, 0), -1 });
}
}
@ -3412,7 +3326,6 @@ ChosenRow InnerWidget::computeChosenRow() const {
bool InnerWidget::chooseRow(
Qt::KeyboardModifiers modifiers,
MsgId pressedTopicRootId) {
_storiesExpandedRequests.fire(false);
if (chooseCollapsedRow()) {
return true;
} else if (chooseHashtag()) {

View File

@ -52,10 +52,6 @@ struct PaintContext;
struct TopicJumpCache;
} // namespace Dialogs::Ui
namespace Dialogs::Stories {
class List;
} // namespace Dialogs::Stories
namespace Dialogs {
class Row;
@ -105,11 +101,6 @@ public:
const QVector<MTPPeer> &my,
const QVector<MTPPeer> &result);
void setTouchScrollActive(bool active);
[[nodiscard]] rpl::producer<bool> storiesExpandedRequests() const;
[[nodiscard]] int defaultScrollTop() const;
void setViewportHeight(int viewportHeight);
[[nodiscard]] FilterId filterId() const;
void clearSelection();
@ -124,7 +115,6 @@ public:
void clearFilter();
void refresh(bool toTop = false);
void refreshForDefaultScroll();
void refreshEmptyLabel();
void resizeEmptyLabel();
@ -322,7 +312,6 @@ private:
void fillArchiveSearchMenu(not_null<Ui::PopupMenu*> menu);
void refreshShownList();
[[nodiscard]] bool storiesShown() const;
[[nodiscard]] int skipTopHeight() const;
[[nodiscard]] int collapsedRowsOffset() const;
[[nodiscard]] int dialogsOffset() const;
@ -409,10 +398,6 @@ private:
const not_null<Window::SessionController*> _controller;
const std::unique_ptr<Stories::List> _stories;
rpl::event_stream<bool> _storiesExpandedRequests;
int _viewportHeight = 0;
not_null<IndexedList*> _shownList;
FilterId _filterId = 0;
bool _mouseSelection = false;

View File

@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "dialogs/dialogs_widget.h"
#include "dialogs/ui/dialogs_stories_content.h"
#include "dialogs/ui/dialogs_stories_list.h"
#include "dialogs/dialogs_inner_widget.h"
#include "dialogs/dialogs_search_from_controllers.h"
#include "dialogs/dialogs_key.h"
@ -64,6 +66,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_changes.h"
#include "data/data_download_manager.h"
#include "data/data_chat_filters.h"
#include "data/data_stories.h"
#include "info/downloads/info_downloads_widget.h"
#include "info/info_memento.h"
#include "styles/style_dialogs.h"
@ -211,15 +214,21 @@ Widget::Widget(
, _cancelSearch(_searchControls, st::dialogsCancelSearch)
, _lockUnlock(_searchControls, st::dialogsLock)
, _scroll(this)
, _allowStoriesExpandTimer([=] {
//_scroll->verticalScrollBar()->setMinimum(0);
})
, _scrollToTop(_scroll, st::dialogsToUp)
, _stories(std::make_unique<Stories::List>(
this,
st::dialogsStoriesList,
Stories::ContentForSession(
&controller->session(),
Data::StorySourcesList::NotHidden)))
, _searchTimer([=] { searchMessages(); })
, _singleMessageSearch(&controller->session()) {
const auto makeChildListShown = [](PeerId peerId, float64 shown) {
return InnerWidget::ChildListShown{ peerId, shown };
};
_scroll->setOverscrollTypes(
Ui::ElasticScroll::OverscrollType::Virtual,
Ui::ElasticScroll::OverscrollType::Real);
_inner = _scroll->setOwnedWidget(object_ptr<InnerWidget>(
this,
controller,
@ -228,6 +237,22 @@ Widget::Widget(
_childListShown.value(),
makeChildListShown)));
_scrollToTop->raise();
rpl::combine(
_scroll->positionValue(),
_scroll->movementValue()
) | rpl::start_with_next([=](
Ui::ElasticScrollPosition position,
Ui::ElasticScrollMovement movement) {
const auto overscrollTop = std::max(-position.overscroll, 0);
if (_overscrollTop != overscrollTop) {
_overscrollTop = overscrollTop;
updateControlsGeometry();
}
using Phase = Ui::ElasticScrollMovement;
_stories->setExpandedHeight(
_overscrollTop,
(movement == Phase::Momentum || movement == Phase::Returning));
}, lifetime());
_inner->updated(
) | rpl::start_with_next([=] {
@ -268,15 +293,6 @@ Widget::Widget(
}
}, lifetime());
_inner->storiesExpandedRequests(
) | rpl::start_with_next([=](bool expanded) {
if (expanded || _scroll->scrollTop() < _inner->defaultScrollTop()) {
if (_scroll->scrollTop() > 0) {
scrollToDefaultChecked(expanded);
}
}
}, lifetime());
_inner->mustScrollTo(
) | rpl::start_with_next([=](const Ui::ScrollToRequest &data) {
if (_scroll) {
@ -328,12 +344,6 @@ Widget::Widget(
) | rpl::start_with_next([=] {
listScrollUpdated();
}, lifetime());
_scroll->setCustomWheelProcess([=](not_null<QWheelEvent*> e) {
return customWheelProcess(e);
});
_scroll->setCustomTouchProcess([=](not_null<QTouchEvent*> e) {
return customTouchProcess(e);
});
session().data().chatsListChanges(
) | rpl::filter([=](Data::Folder *folder) {
@ -383,6 +393,7 @@ Widget::Widget(
setupMainMenuToggle();
setupShortcuts();
setupStories();
_searchForNarrowFilters->setClickedCallback([=] {
_filter->setFocusFast();
@ -589,7 +600,7 @@ void Widget::setupMoreChatsBar() {
}
controller()->activeChatsFilter(
) | rpl::start_with_next([=](FilterId id) {
if (!id) {
if (!id && false) { // #TODO stories testing
_moreChatsBar = nullptr;
updateControlsGeometry();
return;
@ -770,6 +781,38 @@ void Widget::setupMainMenuToggle() {
}, _mainMenu.toggle->lifetime());
}
void Widget::setupStories() {
_stories->clicks(
) | rpl::start_with_next([=](uint64 id) {
controller()->openPeerStories(
PeerId(int64(id)),
Data::StorySourcesList::NotHidden);
}, lifetime());
_stories->showMenuRequests(
) | rpl::start_with_next([=](const Stories::ShowMenuRequest &request) {
FillSourceMenu(controller(), request);
}, lifetime());
_stories->loadMoreRequests(
) | rpl::start_with_next([=] {
session().data().stories().loadMore(
Data::StorySourcesList::NotHidden);
}, lifetime());
_stories->toggleExpandedRequests(
) | rpl::start_with_next([=](bool expanded) {
if (expanded) {
if (!_scrollToAnimation.animating() || _scrollAnimationTo) {
scrollToDefault();
}
}
_scroll->setOverscrollDefaults(
expanded ? -st::dialogsStoriesFull.height : 0,
0);
}, lifetime());
}
void Widget::setupShortcuts() {
Shortcuts::Requests(
) | rpl::filter([=] {
@ -1128,7 +1171,7 @@ void Widget::jumpToTop(bool belowPinned) {
return;
}
if ((currentSearchQuery().trimmed().isEmpty() && !_searchInChat)) {
auto to = _inner->defaultScrollTop();
auto to = 0;
if (belowPinned) {
const auto list = _openedForum
? _openedForum->topicsList()
@ -1156,7 +1199,7 @@ void Widget::scrollToDefault(bool verytop) {
}
_scrollToAnimation.stop();
auto scrollTop = _scroll->scrollTop();
const auto scrollTo = verytop ? 0 : _inner->defaultScrollTop();
const auto scrollTo = 0;
if (scrollTop == scrollTo) {
return;
}
@ -1172,7 +1215,7 @@ void Widget::scrollToDefault(bool verytop) {
const auto animated = qRound(_scrollToAnimation.value(scrollTo));
const auto animatedDelta = animated - scrollTo;
const auto realDelta = _scroll->scrollTop() - scrollTo;
if (realDelta * animatedDelta < 0) {
if (base::OppositeSigns(realDelta, animatedDelta)) {
// We scrolled manually to the other side of target 'scrollTo'.
_scrollToAnimation.stop();
} else if (std::abs(realDelta) > std::abs(animatedDelta)) {
@ -1181,6 +1224,7 @@ void Widget::scrollToDefault(bool verytop) {
}
};
_scrollAnimationTo = scrollTo;
_scrollToAnimation.start(
scroll,
scrollTop,
@ -2398,55 +2442,6 @@ void Widget::completeHashtag(QString tag) {
applyFilterUpdate(true);
}
bool Widget::customWheelProcess(not_null<QWheelEvent*> e) {
customScrollProcess(e->phase());
return false;
}
void Widget::customScrollProcess(Qt::ScrollPhase phase) {
const auto now = _scroll->scrollTop();
const auto def = _inner->defaultScrollTop();
//const auto bar = _scroll->verticalScrollBar();
if (phase == Qt::ScrollBegin || phase == Qt::ScrollUpdate) {
_allowStoriesExpandTimer.cancel();
_inner->setTouchScrollActive(true);
//bar->setMinimum(0);
} else if (phase == Qt::ScrollEnd || phase == Qt::ScrollMomentum) {
_allowStoriesExpandTimer.cancel();
_inner->setTouchScrollActive(false);
if (def > 0 && now >= def) {
//bar->setMinimum(def);
} else {
//bar->setMinimum(0);
}
} else {
const auto allow = (def <= 0)
|| (now < def)
|| (now == def && !_allowStoriesExpandTimer.isActive());
if (allow) {
//_scroll->verticalScrollBar()->setMinimum(0);
_allowStoriesExpandTimer.cancel();
} else {
//bar->setMinimum(def);
_allowStoriesExpandTimer.callOnce(kWaitTillAllowStoriesExpand);
}
}
}
bool Widget::customTouchProcess(not_null<QTouchEvent*> e) {
const auto type = e->type();
customScrollProcess([&] {
switch (e->type()) {
case QEvent::TouchBegin: return Qt::ScrollBegin;
case QEvent::TouchUpdate: return Qt::ScrollUpdate;
case QEvent::TouchEnd:
case QEvent::TouchCancel: return Qt::ScrollEnd;
}
Unexpected("Touch event type in Widdget::customTouchProcess.");
}());
return false;
}
void Widget::resizeEvent(QResizeEvent *e) {
updateControlsGeometry();
}
@ -2526,11 +2521,9 @@ void Widget::updateControlsGeometry() {
? st::dialogsFilterSkip
: (st::dialogsFilterPadding.x() + _mainMenu.toggle->width()))
+ st::dialogsFilterPadding.x();
auto filterRight = (session().domain().local().hasLocalPasscode()
? (st::dialogsFilterPadding.x() + _lockUnlock->width())
: st::dialogsFilterSkip) + st::dialogsFilterPadding.x();
auto filterWidth = qMax(ratiow, smallw) - filterLeft - filterRight;
auto filterAreaHeight = st::topBarHeight;
const auto filterRight = st::dialogsFilterSkip + st::dialogsFilterPadding.x();
const auto filterWidth = qMax(ratiow, smallw) - filterLeft - filterRight;
const auto filterAreaHeight = st::topBarHeight;
_searchControls->setGeometry(0, filterAreaTop, ratiow, filterAreaHeight);
if (_subsectionTopBar) {
_subsectionTopBar->setGeometryWithNarrowRatio(
@ -2542,6 +2535,7 @@ void Widget::updateControlsGeometry() {
auto filterTop = (filterAreaHeight - _filter->height()) / 2;
filterLeft = anim::interpolate(filterLeft, _narrowWidth, narrowRatio);
_filter->setGeometryToLeft(filterLeft, filterTop, filterWidth, _filter->height());
auto mainMenuLeft = anim::interpolate(
st::dialogsFilterPadding.x(),
(_narrowWidth - _mainMenu.toggle->width()) / 2,
@ -2567,14 +2561,21 @@ void Widget::updateControlsGeometry() {
right -= _chooseFromUser->width(); _chooseFromUser->moveToLeft(right, _filter->y());
const auto barw = width();
const auto expandedStoriesTop = filterAreaTop + filterAreaHeight;
const auto storiesHeight = 2 * st::dialogsStories.photoTop
+ st::dialogsStories.photo;
const auto added = (st::dialogsFilter.heightMin - storiesHeight) / 2;
_stories->setLayoutConstraints(
{ filterLeft + filterWidth, filterTop + added },
{ 0, expandedStoriesTop, barw, st::dialogsStoriesFull.height });
if (_forumTopShadow) {
_forumTopShadow->setGeometry(
0,
filterAreaTop + filterAreaHeight,
expandedStoriesTop,
barw,
st::lineWidth);
}
const auto moreChatsBarTop = filterAreaTop + filterAreaHeight;
const auto moreChatsBarTop = expandedStoriesTop + _overscrollTop;
if (_moreChatsBar) {
_moreChatsBar->move(0, moreChatsBarTop);
_moreChatsBar->resizeToWidth(barw);
@ -2599,8 +2600,7 @@ void Widget::updateControlsGeometry() {
auto scrollTop = forumReportTop
+ (_forumReportBar ? _forumReportBar->bar().height() : 0);
const auto wasScrollTop = _scroll->scrollTop();
const auto newScrollTop = (_topDelta < 0
&& wasScrollTop <= _inner->defaultScrollTop())
const auto newScrollTop = (_topDelta < 0 && wasScrollTop <= 0)
? wasScrollTop
: (wasScrollTop + _topDelta);
auto scrollHeight = height() - scrollTop;
@ -2626,19 +2626,13 @@ void Widget::updateControlsGeometry() {
const auto scrollw = _childList ? _narrowWidth : barw;
const auto wasScrollHeight = _scroll->height();
if (scrollHeight >= wasScrollHeight) {
_inner->setViewportHeight(scrollHeight);
}
_scroll->setGeometry(0, scrollTop, scrollw, scrollHeight);
if (scrollHeight < wasScrollHeight) {
_inner->setViewportHeight(scrollHeight);
}
_inner->resize(scrollw, _inner->height());
_inner->setNarrowRatio(narrowRatio);
if (scrollHeight != wasScrollHeight) {
controller()->floatPlayerAreaUpdated();
}
const auto startWithTop = _inner->defaultScrollTop();
const auto startWithTop = 0;
if (wasScrollHeight < startWithTop && scrollHeight >= startWithTop) {
_scroll->scrollToY(startWithTop);
} else if (newScrollTop != wasScrollTop) {
@ -2718,7 +2712,7 @@ void Widget::paintEvent(QPaintEvent *e) {
p.fillRect(above.intersected(r), bg);
}
auto belowTop = _scroll->y() + qMin(_scroll->height(), _inner->height());
auto belowTop = _scroll->y() + _scroll->height();
if (!_widthAnimationCache.isNull()) {
p.drawPixmapLeft(0, _scroll->y(), width(), _widthAnimationCache);
belowTop = _scroll->y() + (_widthAnimationCache.height() / cIntRetinaFactor());

View File

@ -56,6 +56,10 @@ class ConnectionState;
struct SectionShow;
} // namespace Window
namespace Dialogs::Stories {
class List;
} // namespace Dialogs::Stories
namespace Dialogs {
struct RowDescriptor;
@ -136,9 +140,6 @@ private:
void cancelSearchInChat();
void filterCursorMoved();
void completeHashtag(QString tag);
bool customWheelProcess(not_null<QWheelEvent*> e);
bool customTouchProcess(not_null<QTouchEvent*> e);
void customScrollProcess(Qt::ScrollPhase phase);
[[nodiscard]] QString currentSearchQuery() const;
void clearSearchField();
@ -165,6 +166,7 @@ private:
void setupMoreChatsBar();
void setupDownloadBar();
void setupShortcuts();
void setupStories();
[[nodiscard]] bool searchForPeersRequired(const QString &query) const;
[[nodiscard]] bool searchForTopicsRequired(const QString &query) const;
bool setSearchInChat(Key chat, PeerData *from = nullptr);
@ -248,7 +250,6 @@ private:
std::unique_ptr<HistoryView::ContactStatus> _forumReportBar;
object_ptr<Ui::ElasticScroll> _scroll;
base::Timer _allowStoriesExpandTimer;
QPointer<InnerWidget> _inner;
class BottomButton;
object_ptr<BottomButton> _updateTelegram = { nullptr };
@ -257,6 +258,7 @@ private:
std::unique_ptr<Window::ConnectionState> _connecting;
Ui::Animations::Simple _scrollToAnimation;
int _scrollAnimationTo = 0;
std::unique_ptr<Window::SlideAnimation> _showAnimation;
rpl::variable<float64> _shownProgressValue;
@ -265,6 +267,9 @@ private:
bool _scrollToTopIsShown = false;
bool _forumSearchRequested = false;
int _overscrollTop = 0;
std::unique_ptr<Stories::List> _stories;
Data::Folder *_openedFolder = nullptr;
Data::Forum *_openedForum = nullptr;
Dialogs::Key _searchInChat;

View File

@ -24,7 +24,7 @@ constexpr auto kPreloadPages = 2;
constexpr auto kExpandAfterRatio = 0.85;
constexpr auto kCollapseAfterRatio = 0.72;
constexpr auto kFrictionRatio = 0.15;
constexpr auto kSnapExpandedTimeout = crl::time(200);
constexpr auto kExpandCatchUpDuration = crl::time(200);
[[nodiscard]] int AvailableNameWidth(const style::DialogsStoriesList &st) {
const auto &full = st.full;
@ -37,7 +37,6 @@ constexpr auto kSnapExpandedTimeout = crl::time(200);
struct List::Layout {
int itemsCount = 0;
int shownHeight = 0;
float64 expandedRatio = 0.;
float64 ratio = 0.;
float64 thumbnailLeft = 0.;
@ -58,12 +57,9 @@ struct List::Layout {
List::List(
not_null<QWidget*> parent,
const style::DialogsStoriesList &st,
rpl::producer<Content> content,
Fn<int()> shownHeight)
rpl::producer<Content> content)
: RpWidget(parent)
, _st(st)
, _shownHeight(shownHeight)
, _snapExpandedTimer([=] { requestExpanded(_expanded); }) {
, _st(st) {
setCursor(style::cur_default);
std::move(content) | rpl::start_with_next([=](Content &&content) {
@ -87,6 +83,7 @@ void List::showContent(Content &&content) {
return;
}
const auto hidden = _content.elements.empty();
const auto wasCount = int((hidden ? _hidingData : _data).items.size());
_content = std::move(content);
auto items = base::take(
_data.items.empty() ? _hidingData.items : _data.items);
@ -114,6 +111,9 @@ void List::showContent(Content &&content) {
_data.items.emplace_back(Item{ .element = element });
}
}
if (int(_data.items.size()) != wasCount) {
updateGeometry();
}
updateScrollMax();
updateSummary(_data);
update();
@ -253,7 +253,6 @@ rpl::producer<> List::loadMoreRequests() const {
}
void List::requestExpanded(bool expanded) {
_snapExpandedTimer.cancel();
if (_expanded != expanded) {
_expanded = expanded;
_expandedAnimation.start(
@ -274,12 +273,12 @@ void List::resizeEvent(QResizeEvent *e) {
updateScrollMax();
}
void List::updateExpanding(int minHeight, int shownHeight, int fullHeight) {
Expects(shownHeight == minHeight || fullHeight > minHeight);
void List::updateExpanding(int expandingHeight, int expandedHeight) {
Expects(!expandingHeight || expandedHeight > 0);
const auto ratio = (shownHeight == minHeight)
const auto ratio = !expandingHeight
? 0.
: (float64(shownHeight - minHeight) / (fullHeight - minHeight));
: (float64(expandingHeight) / expandedHeight);
if (_lastRatio == ratio) {
return;
}
@ -296,20 +295,12 @@ void List::updateExpanding(int minHeight, int shownHeight, int fullHeight) {
List::Layout List::computeLayout() {
const auto &st = _st.small;
const auto &full = _st.full;
const auto shownHeight = std::max(_shownHeight(), st.height);
if (_lastHeight != shownHeight) {
_lastHeight = shownHeight;
if (_lastHeight == st.height || _lastHeight == full.height) {
_snapExpandedTimer.cancel();
} else if (!_touchScrollActive) {
_snapExpandedTimer.callOnce(kSnapExpandedTimeout);
}
}
updateExpanding(st.height, shownHeight, full.height);
const auto use = _lastExpandedHeight
* _expandCatchUpAnimation.value(1.);
updateExpanding(use, full.height);
const auto expanded = _expandedAnimation.value(_expanded ? 1. : 0.);
const auto expandedRatio = float64(shownHeight - st.height)
/ (full.height - st.height);
const auto expandedRatio = _lastRatio;
const auto collapsedRatio = expandedRatio * kFrictionRatio;
const auto ratio = expandedRatio * expanded
+ collapsedRatio * (1. - expanded);
@ -323,7 +314,7 @@ List::Layout List::computeLayout() {
const auto narrowWidth = st::defaultDialogRow.padding.left()
+ st::defaultDialogRow.photoSize
+ st::defaultDialogRow.padding.left();
const auto narrow = (width() <= narrowWidth);
const auto narrow = false;// (width() <= narrowWidth);
const auto smallSkip = (itemsCount > 1
&& rendering.items[0].element.skipSmall)
? 1
@ -352,7 +343,6 @@ List::Layout List::computeLayout() {
const auto photoLeft = lerp(st.photoLeft, full.photoLeft);
return Layout{
.itemsCount = itemsCount,
.shownHeight = shownHeight,
.expandedRatio = expandedRatio,
.ratio = ratio,
.thumbnailLeft = thumbnailLeft,
@ -391,14 +381,14 @@ void List::paintEvent(QPaintEvent *e) {
auto &rendering = _data.empty() ? _hidingData : _data;
const auto line = elerp(st.lineTwice, full.lineTwice) / 2.;
const auto lineRead = elerp(st.lineReadTwice, full.lineReadTwice) / 2.;
const auto photoTopSmall = (st.height - st.photo) / 2.;
const auto photoTopSmall = st.photoTop;
const auto photoTop = photoTopSmall
+ (full.photoTop - photoTopSmall) * layout.expandedRatio;
const auto photo = lerp(st.photo, full.photo);
const auto summaryTop = st.nameTop
- (st.photoTop + (st.photo / 2.))
+ (photoTop + (photo / 2.));
const auto nameScale = layout.shownHeight / float64(full.height);
const auto nameScale = _lastRatio;
const auto nameTop = nameScale * full.nameTop;
const auto nameWidth = nameScale * AvailableNameWidth(_st);
const auto nameHeight = nameScale * full.nameStyle.font->height;
@ -407,8 +397,18 @@ void List::paintEvent(QPaintEvent *e) {
const auto readUserpicAppearingOpacity = elerp(_st.readOpacity, 0.);
auto p = QPainter(this);
p.fillRect(e->rect(), _bgOverride.value_or(_st.bg));
p.translate(0, height() - layout.shownHeight);
if (_state == State::Changing) {
const auto left = anim::interpolate(
_changingGeometryFrom.x(),
_geometryFull.x(),
layout.expandedRatio);
const auto top = anim::interpolate(
_changingGeometryFrom.y(),
_geometryFull.y(),
layout.expandedRatio);
p.translate(QPoint(left, top) - pos());
}
const auto drawSmall = (expandRatio < 1.);
const auto drawFull = (expandRatio > 0.);
@ -691,6 +691,7 @@ void List::paintSummary(
Data &data,
float64 summaryTop,
float64 hidden) {
#if 0 // #TODO stories to-remove
const auto total = int(data.items.size());
auto &summary = ChooseSummary(
_st.small,
@ -723,6 +724,7 @@ void List::paintSummary(
p.drawImage(
QRectF(left, summaryTop, summaryWidth, summaryHeight),
summary.cache);
#endif
}
void List::wheelEvent(QWheelEvent *e) {
@ -767,12 +769,9 @@ void List::mouseMoveEvent(QMouseEvent *e) {
_lastMousePosition = e->globalPos();
updateSelected();
if (!_dragging && _mouseDownPosition) {
if (!_dragging && _mouseDownPosition && _state == State::Full) {
if ((_lastMousePosition - *_mouseDownPosition).manhattanLength()
>= QApplication::startDragDistance()) {
if (_shownHeight() < _st.full.height) {
requestExpanded(true);
}
_dragging = true;
_startDraggingLeft = _scrollLeft;
}
@ -822,19 +821,82 @@ void List::mouseReleaseEvent(QMouseEvent *e) {
}
}
void List::setBgOverride(QBrush brush) {
_bgOverride = std::move(brush);
void List::setExpandedHeight(int height, bool momentum) {
height = std::clamp(height, 0, _st.full.height);
if (_lastExpandedHeight == height) {
return;
} else if (momentum && _expandIgnored) {
return;
} else if (momentum && height > 0 && !_lastExpandedHeight) {
_expandIgnored = true;
return;
} else if (!momentum && _expandIgnored && height > 0) {
_expandIgnored = false;
_expandCatchUpAnimation.start([=] {
update();
if (!_expandCatchUpAnimation.animating()
&& _lastExpandedHeight == _st.full.height) {
setState(State::Full);
}
}, 0., 1., kExpandCatchUpDuration);
} else if (!height && _expandCatchUpAnimation.animating()) {
_expandCatchUpAnimation.stop();
}
_lastExpandedHeight = height;
setState(!height
? State::Small
: (height < _st.full.height
|| _expandCatchUpAnimation.animating()
|| _expandedAnimation.animating())
? State::Changing
: State::Full);
update();
}
void List::setTouchScrollActive(bool active) {
if (_touchScrollActive != active) {
_touchScrollActive = active;
if (active) {
_snapExpandedTimer.cancel();
} else {
requestExpanded(_expanded);
}
void List::setLayoutConstraints(QPoint topRightSmall, QRect geometryFull) {
_topRightSmall = topRightSmall;
_geometryFull = geometryFull;
updateGeometry();
update();
}
void List::updateGeometry() {
switch (_state) {
case State::Small: setGeometry(countSmallGeometry()); break;
case State::Changing: {
_changingGeometryFrom = countSmallGeometry();
setGeometry(_geometryFull.united(_changingGeometryFrom));
} break;
case State::Full: setGeometry(_geometryFull);
}
update();
}
QRect List::countSmallGeometry() const {
const auto &st = _st.small;
const auto layout = const_cast<List*>(this)->computeLayout();
const auto count = layout.endIndexSmall - layout.startIndexSmall;
const auto width = st.left
+ st.photoLeft
+ st.photo + (count - 1) * st.shift
+ st.photoLeft
+ st.left;
return QRect(
_topRightSmall.x() - width,
_topRightSmall.y(),
width,
st.photoTop + st.photo + st.photoTop);
}
void List::setState(State state) {
if (_state == state) {
return;
}
_state = state;
setAttribute(
Qt::WA_TransparentForMouseEvents,
state == State::Changing);
updateGeometry();
}
void List::contextMenuEvent(QContextMenuEvent *e) {

View File

@ -63,11 +63,10 @@ public:
List(
not_null<QWidget*> parent,
const style::DialogsStoriesList &st,
rpl::producer<Content> content,
Fn<int()> shownHeight);
rpl::producer<Content> content);
void setBgOverride(QBrush brush);
void setTouchScrollActive(bool active);
void setExpandedHeight(int height, bool momentum = false);
void setLayoutConstraints(QPoint topRightSmall, QRect geometryFull);
[[nodiscard]] rpl::producer<uint64> clicks() const;
[[nodiscard]] rpl::producer<ShowMenuRequest> showMenuRequests() const;
@ -77,6 +76,11 @@ public:
private:
struct Layout;
enum class State {
Small,
Changing,
Full,
};
struct Item {
Element element;
QImage nameCache;
@ -149,7 +153,10 @@ private:
void checkLoadMore();
void requestExpanded(bool expanded);
void updateExpanding(int minHeight, int shownHeight, int fullHeight);
void setState(State state);
void updateGeometry();
[[nodiscard]] QRect countSmallGeometry() const;
void updateExpanding(int expandingHeight, int expandedHeight);
void updateHeight();
void toggleAnimated(bool shown);
void paintSummary(
@ -164,14 +171,17 @@ private:
Content _content;
Data _data;
Data _hidingData;
Fn<int()> _shownHeight = 0;
rpl::event_stream<uint64> _clicks;
rpl::event_stream<ShowMenuRequest> _showMenuRequests;
rpl::event_stream<bool> _toggleExpandedRequests;
rpl::event_stream<> _entered;
rpl::event_stream<> _loadMoreRequests;
std::optional<QBrush> _bgOverride;
QPoint _topRightSmall;
QRect _geometryFull;
QRect _changingGeometryFrom;
State _state = State::Small;
Ui::Animations::Simple _shownAnimation;
QPoint _lastMousePosition;
@ -182,11 +192,11 @@ private:
bool _dragging = false;
Ui::Animations::Simple _expandedAnimation;
base::Timer _snapExpandedTimer;
Ui::Animations::Simple _expandCatchUpAnimation;
float64 _lastRatio = 0.;
int _lastHeight = 0;
bool _expanded = false;
bool _touchScrollActive = false;
int _lastExpandedHeight = 0;
bool _expandIgnored : 1 = false;
bool _expanded : 1 = false;
int _selected = -1;
int _pressed = -1;

View File

@ -465,8 +465,7 @@ void TopBar::setStories(rpl::producer<Dialogs::Stories::Content> content) {
last
) | rpl::filter([](const Content &content) {
return !content.elements.empty();
}),
[] { return st::dialogsStories.height; }),
})),
st::infoTopBarScale);
registerToggleControlCallback(
stories,

View File

@ -170,8 +170,7 @@ void InnerWidget::createButtons() {
st::dialogsStoriesListMine,
rpl::duplicate(last) | rpl::filter([](const Content &content) {
return !content.elements.empty();
}),
[] { return st::dialogsStories.height; });
}));
thumbs->show();
rpl::combine(
recent->sizeValue(),

@ -1 +1 @@
Subproject commit 7135f3ed87ee1a6d89cebe6510a2250ffedfb1a9
Subproject commit 80308cea4f57fd13e73abec121325f174ba21883