diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index 8e9f09232..6a20b6cf7 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -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; diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index 2c0ff4141..0a01ddda7 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -141,13 +141,6 @@ InnerWidget::InnerWidget( rpl::producer childListShown) : RpWidget(parent) , _controller(controller) -, _stories(std::make_unique( - 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 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()) { diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h index c72d9667f..608513c85 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h @@ -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 &my, const QVector &result); - void setTouchScrollActive(bool active); - [[nodiscard]] rpl::producer 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 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 _controller; - const std::unique_ptr _stories; - rpl::event_stream _storiesExpandedRequests; - int _viewportHeight = 0; - not_null _shownList; FilterId _filterId = 0; bool _mouseSelection = false; diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index 687aa38cc..10046fa6e 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -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( + 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( 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 e) { - return customWheelProcess(e); - }); - _scroll->setCustomTouchProcess([=](not_null 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 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 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()); diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.h b/Telegram/SourceFiles/dialogs/dialogs_widget.h index ee3861fdb..13ca38398 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.h @@ -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 e); - bool customTouchProcess(not_null 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 _forumReportBar; object_ptr _scroll; - base::Timer _allowStoriesExpandTimer; QPointer _inner; class BottomButton; object_ptr _updateTelegram = { nullptr }; @@ -257,6 +258,7 @@ private: std::unique_ptr _connecting; Ui::Animations::Simple _scrollToAnimation; + int _scrollAnimationTo = 0; std::unique_ptr _showAnimation; rpl::variable _shownProgressValue; @@ -265,6 +267,9 @@ private: bool _scrollToTopIsShown = false; bool _forumSearchRequested = false; + int _overscrollTop = 0; + std::unique_ptr _stories; + Data::Folder *_openedFolder = nullptr; Data::Forum *_openedForum = nullptr; Dialogs::Key _searchInChat; diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.cpp index 23da7e9c8..d7d62b416 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.cpp @@ -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 parent, const style::DialogsStoriesList &st, - rpl::producer content, - Fn shownHeight) + rpl::producer 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(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) { diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.h b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.h index a3b44d04a..0bf355bdb 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.h +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.h @@ -63,11 +63,10 @@ public: List( not_null parent, const style::DialogsStoriesList &st, - rpl::producer content, - Fn shownHeight); + rpl::producer 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 clicks() const; [[nodiscard]] rpl::producer 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 _shownHeight = 0; rpl::event_stream _clicks; rpl::event_stream _showMenuRequests; rpl::event_stream _toggleExpandedRequests; rpl::event_stream<> _entered; rpl::event_stream<> _loadMoreRequests; - std::optional _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; diff --git a/Telegram/SourceFiles/info/info_top_bar.cpp b/Telegram/SourceFiles/info/info_top_bar.cpp index 68aae3228..81f4dd685 100644 --- a/Telegram/SourceFiles/info/info_top_bar.cpp +++ b/Telegram/SourceFiles/info/info_top_bar.cpp @@ -465,8 +465,7 @@ void TopBar::setStories(rpl::producer content) { last ) | rpl::filter([](const Content &content) { return !content.elements.empty(); - }), - [] { return st::dialogsStories.height; }), + })), st::infoTopBarScale); registerToggleControlCallback( stories, diff --git a/Telegram/SourceFiles/info/stories/info_stories_inner_widget.cpp b/Telegram/SourceFiles/info/stories/info_stories_inner_widget.cpp index b6fc6254f..8abc39d8d 100644 --- a/Telegram/SourceFiles/info/stories/info_stories_inner_widget.cpp +++ b/Telegram/SourceFiles/info/stories/info_stories_inner_widget.cpp @@ -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(), diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 7135f3ed8..80308cea4 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 7135f3ed87ee1a6d89cebe6510a2250ffedfb1a9 +Subproject commit 80308cea4f57fd13e73abec121325f174ba21883